@djangocfg/monitor 2.1.420 → 2.1.422

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.
@@ -151,12 +151,13 @@ type MethodFn = <
151
151
 
152
152
  type SseFn = <
153
153
  TData = unknown,
154
- TError = unknown,
154
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
155
+ _TError = unknown,
155
156
  ThrowOnError extends boolean = false,
156
157
  TResponseStyle extends ResponseStyle = 'fields',
157
158
  >(
158
159
  options: Omit<RequestOptions<never, TResponseStyle, ThrowOnError>, 'method'>,
159
- ) => Promise<ServerSentEventsResult<TData, TError>>;
160
+ ) => Promise<ServerSentEventsResult<TData>>;
160
161
 
161
162
  type RequestFn = <
162
163
  TData = unknown,
@@ -118,14 +118,12 @@ const checkForExistence = (
118
118
  return false;
119
119
  };
120
120
 
121
- export const setAuthParams = async ({
122
- security,
123
- ...options
124
- }: Pick<Required<RequestOptions>, 'security'> &
125
- Pick<RequestOptions, 'auth' | 'query'> & {
121
+ export async function setAuthParams(
122
+ options: Pick<RequestOptions, 'auth' | 'query' | 'security'> & {
126
123
  headers: Headers;
127
- }) => {
128
- for (const auth of security) {
124
+ },
125
+ ): Promise<void> {
126
+ for (const auth of options.security ?? []) {
129
127
  if (checkForExistence(options, auth.name)) {
130
128
  continue;
131
129
  }
@@ -154,7 +152,7 @@ export const setAuthParams = async ({
154
152
  break;
155
153
  }
156
154
  }
157
- };
155
+ }
158
156
 
159
157
  export const buildUrl: Client['buildUrl'] = (options) =>
160
158
  getUrl({
@@ -104,10 +104,10 @@ const stripEmptySlots = (params: Params) => {
104
104
 
105
105
  export const buildClientParams = (args: ReadonlyArray<unknown>, fields: FieldsConfig) => {
106
106
  const params: Params = {
107
- body: {},
108
- headers: {},
109
- path: {},
110
- query: {},
107
+ body: Object.create(null),
108
+ headers: Object.create(null),
109
+ path: Object.create(null),
110
+ query: Object.create(null),
111
111
  };
112
112
 
113
113
  const map = buildKeyMap(fields);
@@ -79,12 +79,30 @@ function detectLocale(): string | null {
79
79
  return null;
80
80
  }
81
81
 
82
- /** Default baseUrl from `NEXT_PUBLIC_API_URL`.
82
+ /** Default baseUrl.
83
83
  *
84
- * Both browser and server use NEXT_PUBLIC_API_URL requests go
85
- * directly to Django without Next.js proxy.
84
+ * Browser: uses NEXT_PUBLIC_API_PROXY_URL if set (empty string = same-origin
85
+ * Next.js proxy), otherwise falls back to NEXT_PUBLIC_API_URL directly.
86
+ * Set NEXT_PUBLIC_API_PROXY_URL='' in apps that proxy /apix/* via Next.js
87
+ * Route Handler; leave it unset in apps that hit Django directly.
88
+ *
89
+ * Server (SSR / RSC / Edge): use NEXT_PUBLIC_API_URL so server-side fetch
90
+ * reaches Django directly (proxy is a same-origin HTTP round-trip on server).
86
91
  */
87
92
  function defaultBaseUrl(): string {
93
+ if (typeof window !== 'undefined') {
94
+ try {
95
+ if (typeof process !== 'undefined' && process.env) {
96
+ if (process.env.NEXT_PUBLIC_STATIC_BUILD === 'true') return '';
97
+ // NEXT_PUBLIC_API_PROXY_URL='' means same-origin proxy (e.g. /apix/* route handler).
98
+ // Next.js inlines defined vars as their value; undefined means the var was not set.
99
+ if (process.env.NEXT_PUBLIC_API_PROXY_URL !== undefined)
100
+ return process.env.NEXT_PUBLIC_API_PROXY_URL;
101
+ return process.env.NEXT_PUBLIC_API_URL || '';
102
+ }
103
+ } catch {}
104
+ return '';
105
+ }
88
106
  try {
89
107
  if (typeof process !== 'undefined' && process.env) {
90
108
  if (process.env.NEXT_PUBLIC_STATIC_BUILD === 'true') return '';
@@ -96,13 +114,11 @@ function defaultBaseUrl(): string {
96
114
 
97
115
  /** Default API key fallback from `NEXT_PUBLIC_API_KEY`.
98
116
  *
99
- * In the browser: returns null requests go through the Next.js rewrite
100
- * and the key is injected server-side (never exposed in the bundle).
101
- * On the server: reads NEXT_PUBLIC_API_KEY as a fallback for SSR calls
102
- * that bypass the rewrite (e.g. server components calling Django directly).
117
+ * Both browser and server: if NEXT_PUBLIC_API_KEY is set it is used as a
118
+ * global fallback (e.g. a public demo key). When not set, returns null so
119
+ * per-request keys passed via options.headers take effect unobstructed.
103
120
  */
104
121
  function defaultApiKey(): string | null {
105
- if (isBrowser) return null;
106
122
  try {
107
123
  if (typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_API_KEY) {
108
124
  return process.env.NEXT_PUBLIC_API_KEY;
@@ -286,6 +302,37 @@ async function tryRefresh(): Promise<string | null> {
286
302
  * non-browser environments, so headers populated by the interceptor
287
303
  * are simply absent server-side (which is the correct behaviour
288
304
  * unless the caller explicitly sets a server-side token).
305
+ *
306
+ * ── How API key auth works ────────────────────────────────────────────────
307
+ *
308
+ * There are three ways to supply X-API-Key, in priority order:
309
+ *
310
+ * 1. Per-request header (highest priority, overrides everything):
311
+ * SomeApi.someEndpoint({ headers: { 'X-API-Key': userKey } })
312
+ * Use this in playgrounds or any context where the key comes from
313
+ * user input rather than app config.
314
+ *
315
+ * 2. Global override via auth.setApiKey(key) — applies to every request
316
+ * from this client until cleared. Good for single-user sessions.
317
+ *
318
+ * 3. NEXT_PUBLIC_API_KEY env var — read by defaultApiKey() on every request.
319
+ * Use as a public fallback key (e.g. a shared demo key for unauthenticated
320
+ * visitors). When not set, returns null so per-request keys work freely.
321
+ *
322
+ * ── baseUrl gotcha with proxy packages ───────────────────────────────────
323
+ *
324
+ * If another package (e.g. @carapis/catalog-api/client) calls
325
+ * `auth.setBaseUrl('')` on import, ALL requests from this shared client
326
+ * will go to relative paths (Next.js proxy) instead of hitting Django
327
+ * directly. This is intentional for the public catalog — but breaks any
328
+ * feature that needs to reach Django with a dynamic API key from the
329
+ * browser (e.g. a playground).
330
+ *
331
+ * Fix: pass `baseUrl` explicitly per-call to override the global setting:
332
+ * SomeApi.someEndpoint({
333
+ * baseUrl: process.env.NEXT_PUBLIC_API_URL,
334
+ * headers: { 'X-API-Key': userKey },
335
+ * })
289
336
  */
290
337
  export function installAuthOnClient(client: HeyClient): void {
291
338
  if (_client) return; // idempotent
@@ -303,8 +350,10 @@ export function installAuthOnClient(client: HeyClient): void {
303
350
  const locale = auth.getLocale();
304
351
  if (locale) request.headers.set('Accept-Language', locale);
305
352
 
353
+ // Only set X-API-Key from global store if NOT already present on the
354
+ // request — per-request headers (option 1 above) take precedence.
306
355
  const apiKey = auth.getApiKey();
307
- if (apiKey) request.headers.set('X-API-Key', apiKey);
356
+ if (apiKey && !request.headers.has('X-API-Key')) request.headers.set('X-API-Key', apiKey);
308
357
 
309
358
  try {
310
359
  const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -29,5 +29,11 @@ export { API as CfgMonitorAPI } from './_cfg_monitor';
29
29
  // backend so the SDK class doesn't collide.
30
30
  export { CfgMonitor } from './_cfg_monitor';
31
31
 
32
+ // Generated DTO / schema types (Hey API). The per-group barrels each
33
+ // re-export these too; surfacing them at the top level lets consumers
34
+ // `import { FleetSummary } from '<api>'` without reaching into a
35
+ // group subpath.
36
+ export type * from './types.gen';
37
+
32
38
  // Shared utilities (errors, storage adapters, logger).
33
39
  export * from './helpers';