@heroku/heroku-fetch 0.1.6 → 0.2.0

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
@@ -27,7 +27,7 @@ npm install @heroku/heroku-fetch
27
27
  import { HerokuApiClient } from '@heroku/heroku-fetch';
28
28
 
29
29
  // Create a client for the Platform API
30
- // Automatically uses token from HEROKU_API_KEY env var or ~/.netrc
30
+ // Automatically uses token from the HEROKU_API_KEY env var
31
31
  const client = new HerokuApiClient({
32
32
  service: 'platform',
33
33
  });
@@ -78,29 +78,36 @@ const customClient = new HerokuApiClient({
78
78
 
79
79
  ## Authentication
80
80
 
81
- ### Automatic Token from Environment or Netrc (Default)
81
+ ### Automatic Token from Environment (Default)
82
82
 
83
- By default, the client automatically fetches tokens from `HEROKU_API_KEY` environment variable or `~/.netrc` file. **No manual token management required!**
83
+ By default, the client reads the token from the `HEROKU_API_KEY` environment variable:
84
84
 
85
85
  ```typescript
86
86
  import { HerokuApiClient } from '@heroku/heroku-fetch';
87
87
 
88
- // Token automatically loaded from HEROKU_API_KEY or ~/.netrc
88
+ // Token automatically loaded from HEROKU_API_KEY
89
89
  const client = new HerokuApiClient({
90
90
  service: 'platform',
91
91
  });
92
92
  ```
93
93
 
94
- The token priority is:
95
- 1. `HEROKU_API_KEY` environment variable
96
- 2. `~/.netrc` file (api.heroku.com machine)
97
-
98
- If you need to manually retrieve the token for other purposes, you can use `getAuthToken()`:
94
+ heroku-fetch does not read stored CLI credentials. To authenticate with
95
+ credentials saved by the Heroku CLI (native credential store or `~/.netrc`),
96
+ resolve a token with [`@heroku/heroku-credential-manager`](https://github.com/heroku/heroku-credential-manager)
97
+ and pass it to the client:
99
98
 
100
99
  ```typescript
101
- import { getAuthToken } from '@heroku/heroku-fetch';
100
+ import { HerokuApiClient } from '@heroku/heroku-fetch';
101
+ import { getAuth } from '@heroku/heroku-credential-manager';
102
102
 
103
- const token = getAuthToken(); // Returns string | undefined
103
+ // Pass the account to read the native credential store (Keychain, libsecret,
104
+ // Windows Credential Manager). Passing `undefined` reads `~/.netrc` by host
105
+ // only — it will not find credentials stored solely in the native store.
106
+ const { token } = await getAuth('your-account@example.com', 'api.heroku.com');
107
+ const client = new HerokuApiClient({
108
+ service: 'platform',
109
+ token,
110
+ });
104
111
  ```
105
112
 
106
113
  ### Static Bearer Token
@@ -117,15 +124,6 @@ const client = new HerokuApiClient({
117
124
  Use a function to dynamically retrieve or refresh tokens:
118
125
 
119
126
  ```typescript
120
- import { getAuthTokenProvider } from '@heroku/heroku-fetch';
121
-
122
- // Option 1: Use the built-in provider for dynamic token fetching
123
- const client = new HerokuApiClient({
124
- service: 'platform',
125
- token: getAuthTokenProvider(), // Returns a function that fetches token each time
126
- });
127
-
128
- // Option 2: Provide your own custom token function
129
127
  const client = new HerokuApiClient({
130
128
  service: 'platform',
131
129
  token: async () => {
@@ -487,7 +485,7 @@ import { HerokuApiClient } from '@heroku/heroku-fetch';
487
485
 
488
486
  export default class AppsList extends Command {
489
487
  async run() {
490
- // Client automatically uses token from env or netrc
488
+ // Client automatically uses token from the HEROKU_API_KEY env var
491
489
  const client = new HerokuApiClient({
492
490
  service: 'platform',
493
491
  });
package/dist/browser.d.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  * Browser-specific entry point for @heroku/heroku-fetch
3
3
  *
4
4
  * This entry point excludes Node.js-specific features like:
5
- * - CLI utilities (login, 2FA prompts)
6
- * - File system access (netrc)
5
+ * - CLI utilities (2FA prompts)
7
6
  * - Process environment variables
8
7
  *
9
8
  * Use this entry point when bundling for browser environments.
package/dist/browser.js CHANGED
@@ -2,8 +2,7 @@
2
2
  * Browser-specific entry point for @heroku/heroku-fetch
3
3
  *
4
4
  * This entry point excludes Node.js-specific features like:
5
- * - CLI utilities (login, 2FA prompts)
6
- * - File system access (netrc)
5
+ * - CLI utilities (2FA prompts)
7
6
  * - Process environment variables
8
7
  *
9
8
  * Use this entry point when bundling for browser environments.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { debugError } from '../debug-loggers.js';
7
7
  import { AuthenticationError, HerokuApiError, NotFoundError, RateLimitError, TwoFactorRequiredError, } from '../errors.js';
8
+ import { logScrubbed } from '../redact.js';
8
9
  /**
9
10
  * Parse and handle an error response from the API
10
11
  * Throws appropriate typed error based on status code
@@ -20,7 +21,7 @@ export async function handleErrorResponse(response) {
20
21
  catch {
21
22
  // Failed to parse error body, continue without it
22
23
  }
23
- debugError('Error response: %d %O', response.status, errorBody);
24
+ await logScrubbed(debugError, 'Error response: %d %O', response.status, errorBody);
24
25
  // Check for 2FA error (can be 403 or 412)
25
26
  if (response.status === 412
26
27
  || (response.status === 403 && errorBody?.id === 'two_factor')) {
@@ -5,32 +5,9 @@
5
5
  * used by the ky HTTP client.
6
6
  */
7
7
  import { debugAuth, debugRequest, debugResponse } from '../debug-loggers.js';
8
+ import { logScrubbed } from '../redact.js';
8
9
  import { handleErrorResponse } from './http-error-handler.js';
9
10
  import { handle2FAChallenge, is2FAError } from './two-factor-authentication-handler.js';
10
- /**
11
- * Headers whose values must never appear in debug output. Compared
12
- * case-insensitively. Mirrors the redaction in http-call so tokens
13
- * aren't leaked when `debug` is enabled.
14
- */
15
- const SENSITIVE_HEADERS = new Set([
16
- 'authorization',
17
- 'cookie',
18
- 'heroku-two-factor-code',
19
- 'proxy-authorization',
20
- 'x-addon-sso',
21
- ]);
22
- /**
23
- * Build a plain object of a request's headers with sensitive values
24
- * replaced by `[REDACTED]`, safe for debug logging.
25
- */
26
- function redactHeaders(headers) {
27
- const result = {};
28
- // eslint-disable-next-line unicorn/no-array-for-each
29
- headers.forEach((value, key) => {
30
- result[key] = SENSITIVE_HEADERS.has(key.toLowerCase()) ? '[REDACTED]' : value;
31
- });
32
- return result;
33
- }
34
11
  /**
35
12
  * Create a beforeRequest hook that adds authentication and custom headers
36
13
  */
@@ -65,7 +42,7 @@ export function createBeforeRequestHook(getToken, defaultAccept, customHeaders,
65
42
  }
66
43
  debugRequest('%s %s', request.method, request.url);
67
44
  if (debug) {
68
- debugRequest('Headers: %O', redactHeaders(request.headers));
45
+ await logScrubbed(debugRequest, 'Headers: %O', request.headers);
69
46
  }
70
47
  };
71
48
  }
@@ -1,10 +1,18 @@
1
1
  /**
2
2
  * Default providers for Node.js environments
3
- * These use file system (netrc) and CLI utilities
4
3
  */
5
4
  import type { TokenProvider, TwoFactorOptions } from '../types.js';
6
5
  /**
7
- * Get default token provider for Node.js (uses netrc/env vars)
6
+ * Get default token provider for Node.js (reads the HEROKU_API_KEY env var)
7
+ *
8
+ * heroku-fetch does not read stored credentials. To authenticate with
9
+ * credentials saved by the Heroku CLI, resolve a token with the
10
+ * heroku-credential-manager package and pass it to the client explicitly.
11
+ *
12
+ * When HEROKU_API_KEY is unset this returns `undefined`, so the request is sent
13
+ * with no `Authorization` header rather than failing eagerly — endpoints that
14
+ * need no auth still work, and endpoints that do get a server 401 surfaced as
15
+ * `AuthenticationError`.
8
16
  */
9
17
  export declare function getDefaultTokenProvider(): TokenProvider;
10
18
  /**
@@ -1,20 +1,22 @@
1
1
  /**
2
2
  * Default providers for Node.js environments
3
- * These use file system (netrc) and CLI utilities
4
3
  */
5
4
  import { debugRequest } from '../debug-loggers.js';
5
+ import { logScrubbed } from '../redact.js';
6
6
  /**
7
- * Get default token provider for Node.js (uses netrc/env vars)
7
+ * Get default token provider for Node.js (reads the HEROKU_API_KEY env var)
8
+ *
9
+ * heroku-fetch does not read stored credentials. To authenticate with
10
+ * credentials saved by the Heroku CLI, resolve a token with the
11
+ * heroku-credential-manager package and pass it to the client explicitly.
12
+ *
13
+ * When HEROKU_API_KEY is unset this returns `undefined`, so the request is sent
14
+ * with no `Authorization` header rather than failing eagerly — endpoints that
15
+ * need no auth still work, and endpoints that do get a server 401 surfaced as
16
+ * `AuthenticationError`.
8
17
  */
9
18
  export function getDefaultTokenProvider() {
10
- return async () => {
11
- const { getAuthToken } = await import('../auth/auth.js');
12
- const token = getAuthToken();
13
- if (!token) {
14
- throw new Error('No authentication token found. Please set HEROKU_API_KEY or run: heroku login');
15
- }
16
- return token;
17
- };
19
+ return () => process.env.HEROKU_API_KEY;
18
20
  }
19
21
  /**
20
22
  * Get default 2FA handler for Node.js (uses CLI prompts)
@@ -56,7 +58,7 @@ export async function getDefaultDispatcher() {
56
58
  return new EnvHttpProxyAgent();
57
59
  }
58
60
  catch (error) {
59
- debugRequest('undici not available; skipping dispatcher (%o)', error);
61
+ await logScrubbed(debugRequest, 'undici not available; skipping dispatcher (%o)', error);
60
62
  return undefined;
61
63
  }
62
64
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import ky from 'ky';
7
7
  import { debugAuth, debugError } from '../debug-loggers.js';
8
+ import { logScrubbed } from '../redact.js';
8
9
  /**
9
10
  * Check if a 403 response is a 2FA error by checking the error id
10
11
  */
@@ -47,7 +48,7 @@ export async function handle2FAChallenge(request, twoFactorOptions, twoFactorAtt
47
48
  }
48
49
  catch (error) {
49
50
  twoFactorAttemptedRef.value = false;
50
- debugError('2FA retry failed: %O', error);
51
+ await logScrubbed(debugError, '2FA retry failed: %O', error);
51
52
  throw error;
52
53
  }
53
54
  }
@@ -3,4 +3,3 @@ export declare const debugRequest: debug.Debugger;
3
3
  export declare const debugResponse: debug.Debugger;
4
4
  export declare const debugAuth: debug.Debugger;
5
5
  export declare const debugError: debug.Debugger;
6
- export declare const debugCliLogin: debug.Debugger;
@@ -3,4 +3,3 @@ export const debugRequest = debug('heroku:fetch:request');
3
3
  export const debugResponse = debug('heroku:fetch:response');
4
4
  export const debugAuth = debug('heroku:fetch:auth');
5
5
  export const debugError = debug('heroku:fetch:error');
6
- export const debugCliLogin = debug('heroku:fetch:cli:login');
package/dist/host.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * API host/URL utilities for the Heroku API — resolve the API host and base
3
+ * URL from `HEROKU_HOST`, falling back to the public defaults.
4
+ *
5
+ * Note: heroku-fetch does not read stored credentials. Credential storage and
6
+ * retrieval (netrc, native credential stores) lives in the
7
+ * `@heroku/heroku-credential-manager` package; supply a token to the client
8
+ * explicitly.
9
+ */
10
+ /**
11
+ * Get the API host from environment or default
12
+ *
13
+ * @returns The API host (default: 'api.heroku.com')
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const host = getApiHost();
18
+ * // Returns 'api.heroku.com' or value of HEROKU_HOST env var
19
+ * ```
20
+ */
21
+ export declare function getApiHost(): string;
22
+ /**
23
+ * Get the full API URL from environment or default
24
+ *
25
+ * @returns The API URL (default: 'https://api.heroku.com')
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const url = getApiUrl();
30
+ * // Returns 'https://api.heroku.com' or https://{HEROKU_HOST}
31
+ * ```
32
+ */
33
+ export declare function getApiUrl(): string;
package/dist/host.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * API host/URL utilities for the Heroku API — resolve the API host and base
3
+ * URL from `HEROKU_HOST`, falling back to the public defaults.
4
+ *
5
+ * Note: heroku-fetch does not read stored credentials. Credential storage and
6
+ * retrieval (netrc, native credential stores) lives in the
7
+ * `@heroku/heroku-credential-manager` package; supply a token to the client
8
+ * explicitly.
9
+ */
10
+ /**
11
+ * Get the API host from environment or default
12
+ *
13
+ * @returns The API host (default: 'api.heroku.com')
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const host = getApiHost();
18
+ * // Returns 'api.heroku.com' or value of HEROKU_HOST env var
19
+ * ```
20
+ */
21
+ export function getApiHost() {
22
+ return process.env.HEROKU_HOST || 'api.heroku.com';
23
+ }
24
+ /**
25
+ * Get the full API URL from environment or default
26
+ *
27
+ * @returns The API URL (default: 'https://api.heroku.com')
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const url = getApiUrl();
32
+ * // Returns 'https://api.heroku.com' or https://{HEROKU_HOST}
33
+ * ```
34
+ */
35
+ export function getApiUrl() {
36
+ const host = getApiHost();
37
+ return `https://${host}`;
38
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- export { getApiHost, getApiUrl, getAuthToken, getAuthTokenProvider, } from './auth/auth.js';
2
- export { Login } from './cli/cli-login.js';
3
1
  export { cliTwoFactorPrompt, createCliTwoFactorPrompt, } from './cli/cli-two-factor-prompt.js';
4
2
  export { HerokuApiClient } from './client/index.js';
5
3
  export { AuthenticationError, HerokuApiError, NotFoundError, RateLimitError, TwoFactorRequiredError, } from './errors.js';
4
+ export { getApiHost, getApiUrl, } from './host.js';
6
5
  export type { HerokuApiClientOptions, HerokuError, HerokuErrorResponse, HerokuService, RequestOptions, ServiceConfig, TokenProvider, TwoFactorOptions, } from './types.js';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
- export { getApiHost, getApiUrl, getAuthToken, getAuthTokenProvider, } from './auth/auth.js';
2
- export { Login } from './cli/cli-login.js';
3
1
  export { cliTwoFactorPrompt, createCliTwoFactorPrompt, } from './cli/cli-two-factor-prompt.js';
4
2
  export { HerokuApiClient } from './client/index.js';
5
3
  export { AuthenticationError, HerokuApiError, NotFoundError, RateLimitError, TwoFactorRequiredError, } from './errors.js';
4
+ export { getApiHost, getApiUrl, } from './host.js';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Lazy, fail-closed secret scrubbing for DEBUG-gated log output.
3
+ *
4
+ * Wraps `@heroku/js-blanket`, loaded via dynamic `import()` the first time a
5
+ * debug namespace is enabled so it stays off the default code path.
6
+ */
7
+ /**
8
+ * Scrub a value for debug logging. Handles `Error`, `Headers`, plain objects,
9
+ * and primitives (passed through unchanged — a top-level string is not
10
+ * pattern-scrubbed; `VALUE_PATTERNS` only apply to strings nested inside a
11
+ * scrubbed object/Error). Fail-closed: any failure returns `'[unscrubbable]'`
12
+ * rather than risking the raw value. `depth` is internal, for `cause`
13
+ * recursion.
14
+ */
15
+ export declare function scrubForLog(value: unknown, depth?: number): Promise<unknown>;
16
+ /** A `debug` logger: a format call plus an `enabled` flag for its namespace. */
17
+ export interface DebugLike {
18
+ (formatter: string, ...args: unknown[]): void;
19
+ readonly enabled: boolean;
20
+ }
21
+ /**
22
+ * Scrub each `value` and emit it through `logger` under `formatter`. No-ops
23
+ * when the namespace is disabled. Use this instead of passing raw objects to a
24
+ * `debug` logger with `%O`/`%o` (enforced by lint).
25
+ */
26
+ export declare function logScrubbed(logger: DebugLike, formatter: string, ...values: unknown[]): Promise<void>;
package/dist/redact.js ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Lazy, fail-closed secret scrubbing for DEBUG-gated log output.
3
+ *
4
+ * Wraps `@heroku/js-blanket`, loaded via dynamic `import()` the first time a
5
+ * debug namespace is enabled so it stays off the default code path.
6
+ */
7
+ /** Replacement token for redacted values. */
8
+ const REPLACEMENT = '[REDACTED]';
9
+ /**
10
+ * Extra sensitive keys the preset doesn't cover, matched case-insensitively as
11
+ * substrings. `code` is deliberately excluded so it doesn't also redact
12
+ * `statusCode`/`errorCode` in logged error objects.
13
+ */
14
+ const EXTRA_FIELDS = [
15
+ 'secondfactor',
16
+ 'second_factor',
17
+ 'otp',
18
+ 'sso',
19
+ ];
20
+ /**
21
+ * Redact inline `Bearer`/`Basic` credentials in string leaf values reached
22
+ * while scrubbing an object or Error (e.g. an error message). Bare UUIDs/JWTs
23
+ * are intentionally not matched — they're indistinguishable from benign
24
+ * resource IDs. Note these patterns don't apply to a top-level string passed
25
+ * straight to `scrubForLog`.
26
+ */
27
+ const VALUE_PATTERNS = [
28
+ /\b(?:Bearer|Basic)\s+[\w.~+/=-]+/gi,
29
+ ];
30
+ /** Cap on `error.cause` recursion depth; guards against cyclic chains. */
31
+ const MAX_CAUSE_DEPTH = 8;
32
+ let scrubberPromise;
33
+ /** Build the shared Scrubber once, importing js-blanket lazily. */
34
+ async function getScrubber() {
35
+ if (!scrubberPromise) {
36
+ scrubberPromise = (async () => {
37
+ const [{ Scrubber }, { HEROKU_FIELDS }] = await Promise.all([
38
+ import('@heroku/js-blanket/core/scrubber'),
39
+ import('@heroku/js-blanket/core/presets'),
40
+ ]);
41
+ return new Scrubber({
42
+ fields: [...HEROKU_FIELDS, ...EXTRA_FIELDS],
43
+ patterns: [...VALUE_PATTERNS],
44
+ replacement: REPLACEMENT,
45
+ });
46
+ })();
47
+ // Don't cache a rejected import: clear it so a later call retries rather
48
+ // than degrading all object logging to '[unscrubbable]' for the process.
49
+ scrubberPromise.catch(() => {
50
+ scrubberPromise = undefined;
51
+ });
52
+ }
53
+ return scrubberPromise;
54
+ }
55
+ /**
56
+ * Convert a `Headers` instance to a plain object. Scrubbing one directly yields
57
+ * `{}` (no enumerable own-properties); converting first preserves the entries.
58
+ */
59
+ function headersToObject(headers) {
60
+ const out = {};
61
+ // eslint-disable-next-line unicorn/no-array-for-each
62
+ headers.forEach((value, key) => {
63
+ out[key] = value;
64
+ });
65
+ return out;
66
+ }
67
+ /**
68
+ * Clear credentials embedded in a URL (userinfo and sensitive query values),
69
+ * keeping host/path. Returns the input unchanged if it isn't a parseable URL.
70
+ */
71
+ function redactUrl(url) {
72
+ if (typeof url !== 'string')
73
+ return url;
74
+ try {
75
+ const u = new URL(url);
76
+ if (u.username)
77
+ u.username = REPLACEMENT;
78
+ if (u.password)
79
+ u.password = REPLACEMENT;
80
+ for (const key of u.searchParams.keys()) {
81
+ if (/token|secret|code|sso|key|password|auth|assertion|saml|signature|nonce|otp|credential/i.test(key)) {
82
+ u.searchParams.set(key, REPLACEMENT);
83
+ }
84
+ }
85
+ return u.toString();
86
+ }
87
+ catch {
88
+ return url;
89
+ }
90
+ }
91
+ /**
92
+ * Normalize an `Error` into a plain, scrubbable object. An Error's own
93
+ * `name`/`message`/`stack`/`cause` are non-enumerable and would otherwise be
94
+ * dropped. Nested `Request`/`Response` are summarized rather than passed
95
+ * through: they serialize to `{}` and a `Request` can carry live auth headers.
96
+ */
97
+ async function scrubError(error, depth) {
98
+ const scrubber = await getScrubber();
99
+ // Index signature makes the extra own-properties `unknown` without flattening
100
+ // the known `message`/`name`/`stack` off `Error` (string wins over unknown).
101
+ const err = error;
102
+ const { cause, message, name, request, response, stack, ...rest } = err;
103
+ const normalized = {
104
+ message, name, stack, ...rest,
105
+ };
106
+ if (response && typeof response === 'object') {
107
+ const r = response;
108
+ normalized.response = { status: r.status, statusText: r.statusText, url: redactUrl(r.url) };
109
+ }
110
+ if (request && typeof request === 'object') {
111
+ const r = request;
112
+ normalized.request = { method: r.method, url: redactUrl(r.url) };
113
+ }
114
+ const scrubbed = scrubber.scrub(normalized).data;
115
+ if (cause !== undefined) {
116
+ scrubbed.cause = depth >= MAX_CAUSE_DEPTH
117
+ ? '[max cause depth exceeded]'
118
+ : await scrubForLog(cause, depth + 1);
119
+ }
120
+ return scrubbed;
121
+ }
122
+ /**
123
+ * Scrub a value for debug logging. Handles `Error`, `Headers`, plain objects,
124
+ * and primitives (passed through unchanged — a top-level string is not
125
+ * pattern-scrubbed; `VALUE_PATTERNS` only apply to strings nested inside a
126
+ * scrubbed object/Error). Fail-closed: any failure returns `'[unscrubbable]'`
127
+ * rather than risking the raw value. `depth` is internal, for `cause`
128
+ * recursion.
129
+ */
130
+ export async function scrubForLog(value, depth = 0) {
131
+ try {
132
+ if (value instanceof Error)
133
+ return await scrubError(value, depth);
134
+ if (typeof Headers !== 'undefined' && value instanceof Headers) {
135
+ const scrubber = await getScrubber();
136
+ return scrubber.scrub(headersToObject(value)).data;
137
+ }
138
+ if (value !== null && typeof value === 'object') {
139
+ const scrubber = await getScrubber();
140
+ return scrubber.scrub(value).data;
141
+ }
142
+ return value;
143
+ }
144
+ catch {
145
+ return '[unscrubbable]';
146
+ }
147
+ }
148
+ /**
149
+ * Scrub each `value` and emit it through `logger` under `formatter`. No-ops
150
+ * when the namespace is disabled. Use this instead of passing raw objects to a
151
+ * `debug` logger with `%O`/`%o` (enforced by lint).
152
+ */
153
+ export async function logScrubbed(logger, formatter, ...values) {
154
+ if (!logger.enabled)
155
+ return;
156
+ const scrubbed = await Promise.all(values.map(value => scrubForLog(value)));
157
+ logger(formatter, ...scrubbed);
158
+ }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  export type HerokuService = 'custom' | 'data' | 'particleboard' | 'platform';
2
- export type TokenProvider = (() => Promise<string> | string) | string;
2
+ /**
3
+ * A bearer token, or a function resolving one at request time. A provider may
4
+ * return `undefined` (sync or async) to send the request with no `Authorization`
5
+ * header — appropriate for endpoints or hosts that need no Heroku auth. Requests
6
+ * to endpoints that do require auth then receive a server 401, surfaced as
7
+ * `AuthenticationError`.
8
+ */
9
+ export type TokenProvider = (() => Promise<string | undefined> | string | undefined) | string;
3
10
  export interface TwoFactorOptions {
4
11
  /** Callback function to prompt for 2FA code */
5
12
  onChallenge: () => Promise<string> | string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heroku/heroku-fetch",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "A JavaScript/TypeScript API client for Heroku APIs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -50,14 +50,13 @@
50
50
  },
51
51
  "homepage": "https://github.com/heroku/heroku-fetch#readme",
52
52
  "dependencies": {
53
+ "@heroku/js-blanket": "^1.2.0",
53
54
  "debug": "^4.3.4",
54
55
  "ky": "^2.0.2",
55
56
  "undici": "^8.11.2"
56
57
  },
57
58
  "optionalDependencies": {
58
- "@heroku/heroku-cli-util": "^10.8.0",
59
- "netrc-parser": "^3.1.6",
60
- "open": "^11.0.0"
59
+ "@heroku/heroku-cli-util": "^10.8.0"
61
60
  },
62
61
  "devDependencies": {
63
62
  "@heroku-cli/test-utils": "^1.0.1",
@@ -1,66 +0,0 @@
1
- /**
2
- * Authentication utilities for Heroku API
3
- * Handles token retrieval from environment variables and netrc file
4
- */
5
- /**
6
- * Get the Heroku API token from environment variable or netrc file
7
- *
8
- * Priority:
9
- * 1. HEROKU_API_KEY environment variable
10
- * 2. ~/.netrc file (uses api.heroku.com or HEROKU_HOST if set)
11
- *
12
- * @returns The API token or undefined if not found
13
- *
14
- * @example
15
- * ```typescript
16
- * import { getAuthToken, HerokuApiClient } from '@heroku/heroku-fetch';
17
- *
18
- * const token = getAuthToken();
19
- * const client = new HerokuApiClient({
20
- * service: 'platform',
21
- * token,
22
- * });
23
- * ```
24
- */
25
- export declare function getAuthToken(): string | undefined;
26
- /**
27
- * Get the API host from environment or default
28
- *
29
- * @returns The API host (default: 'api.heroku.com')
30
- *
31
- * @example
32
- * ```typescript
33
- * const host = getApiHost();
34
- * // Returns 'api.heroku.com' or value of HEROKU_HOST env var
35
- * ```
36
- */
37
- export declare function getApiHost(): string;
38
- /**
39
- * Get the full API URL from environment or default
40
- *
41
- * @returns The API URL (default: 'https://api.heroku.com')
42
- *
43
- * @example
44
- * ```typescript
45
- * const url = getApiUrl();
46
- * // Returns 'https://api.heroku.com' or https://{HEROKU_HOST}
47
- * ```
48
- */
49
- export declare function getApiUrl(): string;
50
- /**
51
- * Create a token provider function for use with HerokuApiClient
52
- * This is useful when you want the token to be fetched dynamically
53
- *
54
- * @returns A function that returns the current auth token
55
- *
56
- * @example
57
- * ```typescript
58
- * import { getAuthTokenProvider, HerokuApiClient } from '@heroku/heroku-fetch';
59
- *
60
- * const client = new HerokuApiClient({
61
- * service: 'platform',
62
- * token: getAuthTokenProvider(),
63
- * });
64
- * ```
65
- */
66
- export declare function getAuthTokenProvider(): () => string | undefined;