@sybilion/uilib 1.2.3 → 1.2.4

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.
@@ -0,0 +1,13 @@
1
+ import type { UserConfig } from 'vite';
2
+ export type SybilionStandaloneViteDevOptions = {
3
+ mode: string;
4
+ /** Directory containing `.env*` files. @default process.cwd() */
5
+ envDir?: string;
6
+ /** Prefix proxied to Sybilion API (SDK `apiPrefix`). @default `/api` */
7
+ apiPrefix?: string;
8
+ };
9
+ /**
10
+ * Vite `server` + `preview` fragment for standalone Sybilion SPAs: same-origin `/api` in dev,
11
+ * proxied to `VITE_SYBILION_API_BASE_URL`. Uses `PORT` from env (default `3000`).
12
+ */
13
+ export declare function sybilionStandaloneViteDev(options: SybilionStandaloneViteDevOptions): Pick<UserConfig, 'server' | 'preview'>;
@@ -0,0 +1,49 @@
1
+ import { loadEnv } from 'vite';
2
+
3
+ const DEFAULT_PORT = 3000;
4
+ const SYBILION_API_ENV = 'VITE_SYBILION_API_BASE_URL';
5
+ let warnedMissingApiUrl = false;
6
+ function parsePort(raw) {
7
+ if (raw == null || raw === '')
8
+ return DEFAULT_PORT;
9
+ const n = Number.parseInt(raw, 10);
10
+ if (!Number.isFinite(n) || n <= 0 || n > 65_535)
11
+ return DEFAULT_PORT;
12
+ return n;
13
+ }
14
+ function normalizeApiPrefix(apiPrefix) {
15
+ return apiPrefix.startsWith('/') ? apiPrefix : `/${apiPrefix}`;
16
+ }
17
+ /**
18
+ * Vite `server` + `preview` fragment for standalone Sybilion SPAs: same-origin `/api` in dev,
19
+ * proxied to `VITE_SYBILION_API_BASE_URL`. Uses `PORT` from env (default `3000`).
20
+ */
21
+ function sybilionStandaloneViteDev(options) {
22
+ const envDir = options.envDir ?? process.cwd();
23
+ const apiPrefix = normalizeApiPrefix(options.apiPrefix ?? '/api');
24
+ const env = loadEnv(options.mode, envDir, '');
25
+ const port = parsePort(env.PORT);
26
+ const target = (env[SYBILION_API_ENV] ?? '').replace(/\/$/, '');
27
+ const proxy = {};
28
+ if (target) {
29
+ proxy[apiPrefix] = {
30
+ target,
31
+ changeOrigin: true,
32
+ secure: true,
33
+ };
34
+ }
35
+ else if (options.mode === 'development' && !warnedMissingApiUrl) {
36
+ warnedMissingApiUrl = true;
37
+ console.warn(`[@sybilion/uilib] ${SYBILION_API_ENV} is not set; API dev proxy disabled.`);
38
+ }
39
+ const serverPreview = {
40
+ port,
41
+ proxy,
42
+ };
43
+ return {
44
+ server: serverPreview,
45
+ preview: serverPreview,
46
+ };
47
+ }
48
+
49
+ export { sybilionStandaloneViteDev };
@@ -4,10 +4,15 @@ Greenfield SPA on **your own origin**: `@sybilion/uilib` for layout/UI, `Sybilio
4
4
 
5
5
  **Agents / humans:** use `AppShell` + `AppShellMainContent`; stick to uilib spacing primitives (e.g. `Gap`) instead of ad hoc root horizontal gutters.
6
6
 
7
+ **Local-first:** After scaffolding, the user should run **`yarn dev`** (or `npm run dev`) on **localhost** with **no deploy** (e.g. Vercel) required. Commit **`.env.example`**; the user copies it to **`.env`**. In development, the Sybilion API is reached via a **same-origin `/api` proxy** so the browser avoids CORS; production builds still use `VITE_SYBILION_API_BASE_URL` on the client, and the real API must allow **CORS** for your deployed `Origin` unless you terminate API calls on the same host.
8
+
7
9
  ## 1. Dependencies and global CSS
8
10
 
11
+ Vite-based apps (recommended for new standalone SPAs):
12
+
9
13
  ```bash
10
14
  yarn add react react-dom react-router-dom @auth0/auth0-react @sybilion/uilib @sybilion/sdk
15
+ yarn add -D vite @vitejs/plugin-react
11
16
  ```
12
17
 
13
18
  Import tokens/fonts once (typically `src/main.tsx`):
@@ -18,6 +23,43 @@ import '@sybilion/uilib/standalone-global.css';
18
23
 
19
24
  Mount the tree with `ReactDOM.createRoot` → `App` (wrap with `StrictMode` if you want).
20
25
 
26
+ ### `package.json` scripts (required)
27
+
28
+ Add a **`dev`** script so the app is runnable immediately after clone. Typical Vite setup:
29
+
30
+ ```json
31
+ {
32
+ "scripts": {
33
+ "dev": "vite",
34
+ "build": "vite build",
35
+ "preview": "vite preview"
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### `.env.example`
41
+
42
+ Commit **`.env.example`** at the app root (no secrets). Minimum variables:
43
+
44
+ | Variable | Purpose |
45
+ | -------- | ------- |
46
+ | `PORT` | Vite **dev** and **preview** server port. **`PORT=3000`** matches the current **Auth0 test tenant** (`http://localhost:3000` callback / web origins). Override if your tenant uses another port. |
47
+ | `VITE_SYBILION_API_BASE_URL` | Real Sybilion API origin (no trailing slash). Used as **proxy target** in dev and as SDK `baseUrl` in production builds. |
48
+ | `VITE_AUTH0_DOMAIN` | Auth0 domain (§3). |
49
+ | `VITE_AUTH0_CLIENT_ID` | Auth0 SPA client id (§3). |
50
+
51
+ Example **`.env.example`** content for the app root:
52
+
53
+ ```env
54
+ # Copy to `.env` and fill in values. Do not commit `.env`.
55
+ # PORT: Vite dev/preview bind (sybilionStandaloneViteDev reads this). 3000 matches Auth0 test tenant localhost URLs.
56
+ PORT=3000
57
+
58
+ VITE_SYBILION_API_BASE_URL=https://api-dev.sybilion.com
59
+ VITE_AUTH0_DOMAIN=your-tenant.eu.auth0.com
60
+ VITE_AUTH0_CLIENT_ID=your-spa-client-id
61
+ ```
62
+
21
63
  ## 2. SDK (`@sybilion/sdk`)
22
64
 
23
65
  Typed HTTP client for the Sybilion API. Env vars depend on bundler; for Vite, only `import.meta.env.VITE_*` reaches the client.
@@ -30,7 +72,9 @@ import { createSybilionSDK } from '@sybilion/sdk';
30
72
  export const sybilionJwtStorageKey = 'sybilion.standalone.jwt';
31
73
 
32
74
  export const sybilionSdk = createSybilionSDK({
33
- baseUrl: import.meta.env.VITE_SYBILION_API_BASE_URL as string,
75
+ baseUrl: import.meta.env.DEV
76
+ ? ''
77
+ : (import.meta.env.VITE_SYBILION_API_BASE_URL as string),
34
78
  apiPrefix: '/api',
35
79
  getToken: () =>
36
80
  typeof localStorage !== 'undefined'
@@ -39,7 +83,7 @@ export const sybilionSdk = createSybilionSDK({
39
83
  });
40
84
  ```
41
85
 
42
- **Options:** `baseUrl` — API origin only (no trailing slash). `apiPrefix` — default `'/api'` so calls go to `{baseUrl}/api/v1/...`. `getToken` — must read the same key you pass as `sybilionTokenStorageKey` on `SybilionAuthProvider` (§3).
86
+ **Options:** `baseUrl` — API origin only (no trailing slash) **in production**; in **development** use `''` so requests stay **same-origin** (`/api/v1/...`) and the Vite dev server **proxies `/api`** to `VITE_SYBILION_API_BASE_URL` (see **Local dev: Vite API proxy** below). `apiPrefix` — default `'/api'` so calls go to `{baseUrl}/api/v1/...`. `getToken` — must read the same key you pass as `sybilionTokenStorageKey` on `SybilionAuthProvider` (§3).
43
87
 
44
88
  ```ts
45
89
  import { sybilionSdk } from './libs/sybilion-sdk';
@@ -53,6 +97,27 @@ await sybilionSdk.raw.datasets.getById(datasetId);
53
97
 
54
98
  Package README: [`@sybilion/sdk`](https://www.npmjs.com/package/@sybilion/sdk) — monorepo: [`../../sdk/README.md`](../../sdk/README.md).
55
99
 
100
+ ## Local dev: Vite API proxy
101
+
102
+ Avoid browser CORS in development by serving the SPA from Vite and proxying **`/api`** to the real API. Use **`sybilionStandaloneViteDev`** from `@sybilion/uilib/vite-standalone-dev` in **`vite.config.ts`**: it reads **`PORT`** (defaults to **3000** if unset or invalid) for **`server`** / **`preview`**, and sets **`proxy['/api']`** → **`VITE_SYBILION_API_BASE_URL`** with `changeOrigin` and `secure: true`.
103
+
104
+ ```ts
105
+ import { defineConfig } from 'vite';
106
+ import react from '@vitejs/plugin-react';
107
+ import { sybilionStandaloneViteDev } from '@sybilion/uilib/vite-standalone-dev';
108
+
109
+ export default defineConfig(({ mode }) => ({
110
+ ...sybilionStandaloneViteDev({ mode }),
111
+ plugins: [react()],
112
+ }));
113
+ ```
114
+
115
+ Combine with an **empty `baseUrl` in dev** in the SDK module (§2). In **`preview`** builds, keep **`.env`** with **`VITE_SYBILION_API_BASE_URL`** so `vite preview` can still proxy API calls locally.
116
+
117
+ ## Local dev: apps with a Go server
118
+
119
+ Some templates include a **Go** server. This repo does **not** provide Go middleware. The contract: whatever serves the **same origin the browser uses for the SPA** must **reverse-proxy** path prefix **`/api`** (or your SDK `apiPrefix`) to the Sybilion API. Use **`baseUrl: ''`** in dev when those requests are same-origin. Name and document server-side env vars (e.g. API upstream URL) in the Go project.
120
+
56
121
  ## 3. Auth (`SybilionAuthProvider`)
57
122
 
58
123
  Use inside `BrowserRouter` if redirects hit a callback route.
@@ -267,12 +332,15 @@ Composition: `PageScroll` → `AppShell` → `AppSidebar` → `AppShellMainConte
267
332
 
268
333
  ### Greenfield checklist (agents)
269
334
 
270
- | Step | Deliverable |
271
- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
272
- | Env | `VITE_SYBILION_API_BASE_URL`, `VITE_AUTH0_DOMAIN`, `VITE_AUTH0_CLIENT_ID` (names mirror §2–§3). |
273
- | Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route `element` pages under e.g. `src/pages/`. |
274
- | Auth0 | SPA callback / logout URLs + allowed web origins your deploy URLs (and previews). |
275
- | API | Sybilion backend CORS same `Origin` values. |
335
+ | Step | Deliverable |
336
+ | ---- | ----------- |
337
+ | Env files | **`.env.example`** (committed) + **`.env`** locally; **`PORT=3000`** recommended for Auth0 test tenant; **`VITE_*`** as in §1 table. |
338
+ | Scripts | **`package.json`** includes mandatory **`dev`** (e.g. `"vite"`); optional **`build`**, **`preview`**. |
339
+ | Vite proxy | **`vite.config.ts`** spreads **`sybilionStandaloneViteDev({ mode })`**; SDK **`baseUrl`** empty in dev (§2). |
340
+ | Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route pages under e.g. `src/pages/`. |
341
+ | Auth0 | SPA callback / logout URLs + allowed web origins → **`http://localhost:<PORT>`** for local dev and deploy URLs (and previews). |
342
+ | API | **Dev:** proxy handles API traffic (no browser CORS to API). **Prod:** Sybilion backend **CORS** → your deploy `Origin`, unless API is same-origin. |
343
+ | Go (if applicable) | Server proxies **`/api`** to Sybilion; SPA dev **`baseUrl`** stays `''` when same-origin. |
276
344
 
277
345
  ### Glossary (high-use pieces)
278
346
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sybilion/uilib",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "Sybilion Design System — React UI components (Webpack + Stylus)",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -30,7 +30,12 @@
30
30
  "default": "./src/index.ts"
31
31
  },
32
32
  "./src/*": "./src/*",
33
- "./standalone-global.css": "./assets/standalone-global.css"
33
+ "./standalone-global.css": "./assets/standalone-global.css",
34
+ "./vite-standalone-dev": {
35
+ "types": "./dist/standalone/vite-sybilion-standalone-dev.d.ts",
36
+ "import": "./dist/standalone/vite-sybilion-standalone-dev.js",
37
+ "default": "./dist/standalone/vite-sybilion-standalone-dev.js"
38
+ }
34
39
  },
35
40
  "files": [
36
41
  "assets",
@@ -109,7 +114,8 @@
109
114
  "@sybilion/sdk": ">=0.0.1",
110
115
  "react": ">=18.0.0",
111
116
  "react-dom": ">=18.0.0",
112
- "react-router-dom": ">=6.0.0"
117
+ "react-router-dom": ">=6.0.0",
118
+ "vite": "^6.0.0"
113
119
  },
114
120
  "peerDependenciesMeta": {
115
121
  "@auth0/auth0-react": {
@@ -117,6 +123,9 @@
117
123
  },
118
124
  "@sybilion/sdk": {
119
125
  "optional": true
126
+ },
127
+ "vite": {
128
+ "optional": true
120
129
  }
121
130
  },
122
131
  "devDependencies": {
@@ -178,6 +187,7 @@
178
187
  "ts-jest": "^29.2.5",
179
188
  "ts-node": "^10.9.1",
180
189
  "typescript": "^5.3.3",
190
+ "vite": "^6.0.0",
181
191
  "webpack": "^5.75.0",
182
192
  "webpack-cli": "^5.0.1",
183
193
  "webpack-dev-server": "^5.2.3"
@@ -0,0 +1,65 @@
1
+ import type { UserConfig } from 'vite';
2
+ import { loadEnv } from 'vite';
3
+
4
+ const DEFAULT_PORT = 3000;
5
+ const SYBILION_API_ENV = 'VITE_SYBILION_API_BASE_URL';
6
+
7
+ export type SybilionStandaloneViteDevOptions = {
8
+ mode: string;
9
+ /** Directory containing `.env*` files. @default process.cwd() */
10
+ envDir?: string;
11
+ /** Prefix proxied to Sybilion API (SDK `apiPrefix`). @default `/api` */
12
+ apiPrefix?: string;
13
+ };
14
+
15
+ let warnedMissingApiUrl = false;
16
+
17
+ function parsePort(raw: string | undefined): number {
18
+ if (raw == null || raw === '') return DEFAULT_PORT;
19
+ const n = Number.parseInt(raw, 10);
20
+ if (!Number.isFinite(n) || n <= 0 || n > 65_535) return DEFAULT_PORT;
21
+ return n;
22
+ }
23
+
24
+ function normalizeApiPrefix(apiPrefix: string): string {
25
+ return apiPrefix.startsWith('/') ? apiPrefix : `/${apiPrefix}`;
26
+ }
27
+
28
+ /**
29
+ * Vite `server` + `preview` fragment for standalone Sybilion SPAs: same-origin `/api` in dev,
30
+ * proxied to `VITE_SYBILION_API_BASE_URL`. Uses `PORT` from env (default `3000`).
31
+ */
32
+ export function sybilionStandaloneViteDev(
33
+ options: SybilionStandaloneViteDevOptions,
34
+ ): Pick<UserConfig, 'server' | 'preview'> {
35
+ const envDir = options.envDir ?? process.cwd();
36
+ const apiPrefix = normalizeApiPrefix(options.apiPrefix ?? '/api');
37
+ const env = loadEnv(options.mode, envDir, '');
38
+ const port = parsePort(env.PORT);
39
+ const target = (env[SYBILION_API_ENV] ?? '').replace(/\/$/, '');
40
+
41
+ const proxy: NonNullable<UserConfig['server']>['proxy'] = {};
42
+
43
+ if (target) {
44
+ proxy[apiPrefix] = {
45
+ target,
46
+ changeOrigin: true,
47
+ secure: true,
48
+ };
49
+ } else if (options.mode === 'development' && !warnedMissingApiUrl) {
50
+ warnedMissingApiUrl = true;
51
+ console.warn(
52
+ `[@sybilion/uilib] ${SYBILION_API_ENV} is not set; API dev proxy disabled.`,
53
+ );
54
+ }
55
+
56
+ const serverPreview = {
57
+ port,
58
+ proxy,
59
+ };
60
+
61
+ return {
62
+ server: serverPreview,
63
+ preview: serverPreview,
64
+ };
65
+ }