@checkstack/frontend-api 0.3.6 → 0.3.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @checkstack/frontend-api
2
2
 
3
+ ## 0.3.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 67158e2: Standardize package metadata, unify AJV versions to 8.18.0, and enforce monorepo architecture rules via updated ESLint configuration. This ensures consistent package discovery and runtime dependency safety across the platform.
8
+ - Updated dependencies [67158e2]
9
+ - @checkstack/common@0.6.4
10
+
11
+ ## 0.3.7
12
+
13
+ ### Patch Changes
14
+
15
+ - 0603d39: Fix onboarding flow not appearing on fresh Docker deployments (issue #79)
16
+
17
+ The `.env.example` had `BASE_URL` defaulting to `http://localhost:5173`
18
+ (the Vite dev server port). Users copying this file verbatim for a Docker
19
+ deployment would get a frontend that silently made all API calls to the
20
+ wrong origin, causing empty state and extreme sluggishness.
21
+
22
+ **Changes:**
23
+
24
+ - `.env.example`: Adds clear comments explaining the value must match the
25
+ container's exposed port.
26
+ - `frontend-api` (`RuntimeConfigProvider`): Removes the silent fallback when
27
+ `/api/config` returns an unreachable baseUrl — instead propagates the error
28
+ so it can be surfaced.
29
+ - `frontend` (`App.tsx`): Renders an actionable error screen when the backend
30
+ config cannot be loaded, showing the exact `BASE_URL` fix and the
31
+ `docker compose` command to recover.
32
+ - `docs/getting-started/docker.md`: Adds a dedicated troubleshooting section
33
+ for this exact misconfiguration.
34
+
3
35
  ## 0.3.6
4
36
 
5
37
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/frontend-api",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
@@ -12,8 +12,8 @@
12
12
  "react": "^18.0.0"
13
13
  },
14
14
  "dependencies": {
15
- "@checkstack/common": "0.6.2",
16
- "@orpc/client": "^1.13.2",
15
+ "@checkstack/common": "0.6.3",
16
+ "@orpc/client": "^1.13.14",
17
17
  "@orpc/react-query": "1.13.4",
18
18
  "@orpc/tanstack-query": "^1.13.2",
19
19
  "@tanstack/react-query": "^5.64.0"
@@ -24,5 +24,8 @@
24
24
  "typescript": "^5.0.0",
25
25
  "@checkstack/tsconfig": "0.0.3",
26
26
  "@checkstack/scripts": "0.1.1"
27
+ },
28
+ "checkstack": {
29
+ "type": "tooling"
27
30
  }
28
31
  }
@@ -42,8 +42,34 @@ interface RuntimeConfigProviderProps {
42
42
  }
43
43
 
44
44
  /**
45
- * Fetches runtime config from backend on mount.
46
- * All consumers should wait for config to load before rendering.
45
+ * Safely reads the current page origin without requiring the DOM lib
46
+ * to be in scope in every consuming package's tsconfig.
47
+ *
48
+ * Accesses `location` via `globalThis` cast to avoid the untyped-global
49
+ * error that appears when non-browser packages (e.g. catalog-common) run
50
+ * TypeScript against this file and their tsconfig doesn't include "DOM".
51
+ */
52
+ function getCurrentOrigin(): string | undefined {
53
+ const g = globalThis as Record<string, unknown>;
54
+ const loc = g["location"] as { origin?: string } | undefined;
55
+ const origin = loc?.origin;
56
+ return typeof origin === "string" && origin.length > 0 ? origin : undefined;
57
+ }
58
+
59
+ /**
60
+ * Fetches runtime config from the backend on mount, then validates the returned
61
+ * `baseUrl` is actually reachable.
62
+ *
63
+ * Why the probe step matters: `/api/config` is always fetched from the current
64
+ * origin and always succeeds — but the `baseUrl` it returns could point to the
65
+ * wrong port (e.g. the Vite dev server) if `BASE_URL` is misconfigured.
66
+ * Without the probe, all subsequent API calls silently fail with CORS/network
67
+ * errors and the user sees an empty, unresponsive dashboard (issue #79).
68
+ *
69
+ * Probe logic:
70
+ * - If `baseUrl` matches the current page origin → trivially reachable, skip probe.
71
+ * - Otherwise → HEAD-request `${baseUrl}/api/config` with a 5 s timeout.
72
+ * A connection error here means the baseUrl is wrong.
47
73
  */
48
74
  export const RuntimeConfigProvider: React.FC<RuntimeConfigProviderProps> = ({
49
75
  children,
@@ -55,28 +81,50 @@ export const RuntimeConfigProvider: React.FC<RuntimeConfigProviderProps> = ({
55
81
  useEffect(() => {
56
82
  const fetchConfig = async () => {
57
83
  try {
58
- // In development, the proxy handles /api routes
59
- // In production, this is the same origin
84
+ // Step 1: Fetch config from same-origin backend (always succeeds).
60
85
  const response = await fetch("/api/config");
61
86
  if (!response.ok) {
62
- throw new Error(`Failed to fetch config: ${response.status}`);
87
+ throw new Error(`Failed to fetch runtime config: ${response.status}`);
63
88
  }
64
89
  const data = (await response.json()) as RuntimeConfig;
65
- cachedConfig = data; // Populate module-level cache
90
+
91
+ // Step 2: Validate the returned baseUrl is actually reachable.
92
+ // We only need to probe when baseUrl differs from the current page
93
+ // origin — same-origin is trivially reachable.
94
+ const currentOrigin = getCurrentOrigin();
95
+ if (currentOrigin && data.baseUrl !== currentOrigin) {
96
+ try {
97
+ await fetch(`${data.baseUrl}/api/config`, {
98
+ method: "HEAD",
99
+ signal: AbortSignal.timeout(5000),
100
+ });
101
+ } catch {
102
+ // baseUrl is not reachable — misconfigured BASE_URL env var.
103
+ setError(
104
+ new Error(
105
+ `BASE_URL is set to "${data.baseUrl}" but it cannot be reached. ` +
106
+ `Update BASE_URL in your .env to match the port your container exposes (e.g. ${currentOrigin}).`
107
+ )
108
+ );
109
+ // Don't set config so the error screen renders.
110
+ return;
111
+ }
112
+ }
113
+
114
+ cachedConfig = data;
66
115
  setConfig(data);
67
116
  } catch (error_) {
68
117
  console.error("RuntimeConfigProvider: Failed to load config", error_);
69
- // Fallback to localhost for development
70
- const fallback = { baseUrl: "http://localhost:3000" };
71
- cachedConfig = fallback; // Populate cache even on error
72
- setConfig(fallback);
73
- setError(error_ instanceof Error ? error_ : new Error(String(error_)));
118
+ setError(
119
+ error_ instanceof Error ? error_ : new Error(String(error_))
120
+ );
121
+ // Do NOT set a fallback — surface the error visibly.
74
122
  } finally {
75
123
  setIsLoading(false);
76
124
  }
77
125
  };
78
126
 
79
- fetchConfig();
127
+ void fetchConfig();
80
128
  }, []);
81
129
 
82
130
  return (
@@ -91,15 +139,15 @@ export const RuntimeConfigProvider: React.FC<RuntimeConfigProviderProps> = ({
91
139
  // =============================================================================
92
140
 
93
141
  /**
94
- * Access the runtime config context.
95
- * Returns { config, isLoading, error }.
142
+ * Access the runtime config. Consumers should check isLoading first.
96
143
  */
97
144
  export function useRuntimeConfig(): RuntimeConfig {
98
145
  const { config, isLoading } = useContext(RuntimeConfigContext);
99
146
 
100
147
  if (isLoading || !config) {
101
- // Return fallback during loading - consumers should check isLoading
102
- return { baseUrl: "http://localhost:3000" };
148
+ // Return a safe fallback while loading consumers must check isLoading
149
+ // if they need to block on the config being available.
150
+ return { baseUrl: getCurrentOrigin() ?? "http://localhost:3000" };
103
151
  }
104
152
 
105
153
  return config;