@vibedash/client 0.3.0 → 0.4.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.
@@ -30,8 +30,23 @@ async function initDuckDB() {
30
30
  async loadParquet(name, url, forceReload = false) {
31
31
  if (loadedTables.has(name) && !forceReload)
32
32
  return;
33
+ // Validate URL origin — only allow same-origin or *.vibedash.xyz
34
+ const parsed = new URL(url, window.location.origin);
35
+ const isSameOrigin = parsed.origin === window.location.origin;
36
+ const isVibedash = parsed.hostname.endsWith(".vibedash.xyz");
37
+ if (!isSameOrigin && !isVibedash) {
38
+ throw new Error("Parquet URL origin not allowed");
39
+ }
33
40
  // Fetch the Parquet file and register it
34
41
  const response = await fetch(url);
42
+ if (!response.ok) {
43
+ throw new Error(`Failed to fetch Parquet: ${response.status}`);
44
+ }
45
+ // Reject files larger than 100MB to prevent browser OOM
46
+ const contentLength = response.headers.get("Content-Length");
47
+ if (contentLength && parseInt(contentLength, 10) > 100 * 1024 * 1024) {
48
+ throw new Error("Parquet file too large (>100MB)");
49
+ }
35
50
  const buffer = await response.arrayBuffer();
36
51
  await db.registerFileBuffer(`${name}.parquet`, new Uint8Array(buffer));
37
52
  // Drop existing table on force reload so we pick up the new data
@@ -1,11 +1,13 @@
1
1
  import type { VibeDashConfig } from "./types";
2
2
  type RefetchFn = () => Promise<void>;
3
- interface VibeDashContextValue extends VibeDashConfig {
3
+ interface VibeDashContextValue extends Omit<VibeDashConfig, "apiUrl"> {
4
+ apiUrl: string;
5
+ useCredentials: boolean;
4
6
  registerQuery: (name: string, fn: RefetchFn, cachedAt: Date | null) => void;
5
7
  unregisterQuery: (name: string) => void;
6
8
  refreshAll: () => Promise<void>;
7
9
  }
8
- export declare function VibeDashProvider({ apiUrl, projectSlug, dashboardSlug, apiKey, children, }: VibeDashConfig & {
10
+ export declare function VibeDashProvider({ apiUrl: apiUrlProp, projectSlug, dashboardSlug, apiKey, children, }: VibeDashConfig & {
9
11
  children: React.ReactNode;
10
12
  }): import("react/jsx-runtime").JSX.Element;
11
13
  export declare function useVibeDash(): VibeDashContextValue;
package/dist/provider.js CHANGED
@@ -1,9 +1,29 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useCallback, useRef, useEffect, useState, } from "react";
3
+ const HUB_API_URL = "https://vibedash.xyz/api";
4
+ function resolveApiConfig(apiUrlProp, apiKey) {
5
+ // Explicit override — backward compat for existing dashboards
6
+ if (apiUrlProp) {
7
+ return { apiUrl: apiUrlProp, useCredentials: !apiKey };
8
+ }
9
+ // SSR guard
10
+ if (typeof window === "undefined") {
11
+ return { apiUrl: HUB_API_URL, useCredentials: false };
12
+ }
13
+ const hostname = window.location.hostname;
14
+ const isLocalhost = hostname === "localhost" || hostname === "127.0.0.1";
15
+ if (isLocalhost) {
16
+ // Local dev — Hub API directly with Bearer token
17
+ return { apiUrl: HUB_API_URL, useCredentials: false };
18
+ }
19
+ // Production (*.vibedash.xyz or custom domain) — Worker proxy
20
+ return { apiUrl: "/_api", useCredentials: true };
21
+ }
3
22
  const VibeDashContext = createContext(null);
4
23
  const POLL_INTERVAL_ACTIVE = 30000; // 30s when caching is on
5
24
  const POLL_INTERVAL_IDLE = 300000; // 5min when caching is off
6
- export function VibeDashProvider({ apiUrl, projectSlug, dashboardSlug, apiKey, children, }) {
25
+ export function VibeDashProvider({ apiUrl: apiUrlProp, projectSlug, dashboardSlug, apiKey, children, }) {
26
+ const { apiUrl, useCredentials } = resolveApiConfig(apiUrlProp, apiKey);
7
27
  const queryMap = useRef(new Map());
8
28
  const [pollInterval, setPollInterval] = useState(POLL_INTERVAL_IDLE);
9
29
  const registerQuery = useCallback((name, fn, cachedAt) => {
@@ -35,7 +55,7 @@ export function VibeDashProvider({ apiUrl, projectSlug, dashboardSlug, apiKey, c
35
55
  const res = await fetch(`${apiUrl}/query/status`, {
36
56
  method: "POST",
37
57
  headers,
38
- credentials: apiKey ? "omit" : "include",
58
+ credentials: useCredentials ? "include" : "omit",
39
59
  body: JSON.stringify({
40
60
  project_slug: projectSlug,
41
61
  dashboard_slug: dashboardSlug,
@@ -78,12 +98,13 @@ export function VibeDashProvider({ apiUrl, projectSlug, dashboardSlug, apiKey, c
78
98
  cancelled = true;
79
99
  clearTimeout(timeoutId);
80
100
  };
81
- }, [apiUrl, projectSlug, dashboardSlug, apiKey, pollInterval]);
101
+ }, [apiUrl, projectSlug, dashboardSlug, apiKey, useCredentials, pollInterval]);
82
102
  return (_jsx(VibeDashContext.Provider, { value: {
83
103
  apiUrl,
84
104
  projectSlug,
85
105
  dashboardSlug,
86
106
  apiKey,
107
+ useCredentials,
87
108
  registerQuery,
88
109
  unregisterQuery,
89
110
  refreshAll,
@@ -93,7 +114,7 @@ export function useVibeDash() {
93
114
  const ctx = useContext(VibeDashContext);
94
115
  if (!ctx) {
95
116
  throw new Error("useVibeDash must be used within a <VibeDashProvider>. " +
96
- 'Wrap your app in <VibeDashProvider apiUrl="..." projectSlug="...">.');
117
+ 'Wrap your app in <VibeDashProvider projectSlug="...">.');
97
118
  }
98
119
  return ctx;
99
120
  }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export interface VibeDashConfig {
2
- /** The Vibe Dash API URL (defaults to https://hub.vibedash.xyz/api) */
3
- apiUrl: string;
2
+ /**
3
+ * API URL override. If omitted, auto-detected:
4
+ * - *.vibedash.xyz or custom domain → "/_api" (Worker proxy)
5
+ * - localhost / 127.0.0.1 → "https://vibedash.xyz/api" (direct)
6
+ */
7
+ apiUrl?: string;
4
8
  /** The project slug (e.g. "data-man") */
5
9
  projectSlug: string;
6
10
  /** The dashboard slug — enables auto-polling for live cache updates */
package/dist/use-query.js CHANGED
@@ -26,7 +26,7 @@ import { useVibeDash } from "./provider";
26
26
  * ```
27
27
  */
28
28
  export function useQuery(queryName, options) {
29
- const { apiUrl, projectSlug, apiKey, registerQuery, unregisterQuery } = useVibeDash();
29
+ const { apiUrl, projectSlug, apiKey, useCredentials, registerQuery, unregisterQuery } = useVibeDash();
30
30
  const [data, setData] = useState(null);
31
31
  const [loading, setLoading] = useState(false);
32
32
  const [error, setError] = useState(null);
@@ -48,7 +48,7 @@ export function useQuery(queryName, options) {
48
48
  const res = await fetch(`${apiUrl}/query`, {
49
49
  method: "POST",
50
50
  headers,
51
- credentials: apiKey ? "omit" : "include",
51
+ credentials: useCredentials ? "include" : "omit",
52
52
  body: JSON.stringify({
53
53
  project_slug: projectSlug,
54
54
  query_name: queryName,
@@ -92,7 +92,7 @@ export function useQuery(queryName, options) {
92
92
  finally {
93
93
  setLoading(false);
94
94
  }
95
- }, [apiUrl, projectSlug, queryName, apiKey]);
95
+ }, [apiUrl, projectSlug, queryName, apiKey, useCredentials]);
96
96
  // Register this query by name so the provider can:
97
97
  // 1. Call refetch via useRefreshAll()
98
98
  // 2. Detect stale data via auto-polling and trigger refetch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedash/client",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "React hooks for Vibe Dash dashboards — useQuery() with optional DuckDB-WASM caching",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",