@bardioc/app-sdk 0.4.2 → 0.4.3

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
@@ -56,7 +56,7 @@ createRoot(document.getElementById('root')!).render(
56
56
  | --------------------------- | ------------------------------------------- |
57
57
  | `@bardioc/app-sdk` | `createHostBridge`, errors, types, manifest |
58
58
  | `@bardioc/app-sdk/react` | `AppSdkProvider`, hooks (`useSdk`, etc.) |
59
- | `@bardioc/app-sdk/dev` | host-backed standalone dev-session helpers |
59
+ | `@bardioc/app-sdk/dev` | standalone dev bridge helpers |
60
60
  | `@bardioc/app-sdk/types` | Type-only TypeScript exports |
61
61
  | `@bardioc/app-sdk/protocol` | `SDK_MSG` constants, wire format types |
62
62
  | `@bardioc/app-sdk/vite` | `bardiocApp()` Vite plugin |
@@ -91,12 +91,10 @@ import {
91
91
  useSdk,
92
92
  useNotify,
93
93
  useSendToKernel,
94
- useStandaloneDevLogout,
95
94
  } from '@bardioc/app-sdk/react';
96
95
 
97
96
  // Standalone dev helpers
98
97
  import {
99
- ensureDevAuthSession,
100
98
  installDevBridge,
101
99
  isDevStandalone,
102
100
  isInsideIframe,
@@ -137,12 +135,10 @@ New apps scaffolded with `create-bardioc-app` include the `<link>` already.
137
135
 
138
136
  Standalone local development is host-backed only.
139
137
 
140
- 1. Create or open the app in the host App Store so you have its `VITE_APP_ID`.
141
- 2. In the host App Store, open the app details and use `Copy Dev Session`.
142
- 3. Paste the snippet into your local `.env`.
143
- 4. Set `VITE_ENABLE_DEV_AUTH=true` and run `pnpm dev`.
138
+ 1. Set `DEV_SECRET` and `API_BASE_URL` in your local `.env`.
139
+ 2. Run `pnpm dev`.
144
140
 
145
- That flow stores a host-issued dev session locally and routes transport calls through the host `/api/transport` endpoint. There is no separate local token exchange mode anymore.
141
+ The Vite plugin proxies `/rest/*` requests to `API_BASE_URL` with the `X-Dev-Secret` header, and `installDevBridge()` handles SDK messages when running in standalone dev.
146
142
 
147
143
  For public testing against a live URL, run `pnpm dev:live` and use the emitted tunnel URL as the app's live URL in the host App Store configuration.
148
144
 
@@ -151,7 +147,6 @@ For public testing against a live URL, run `pnpm dev:live` and use the emitted t
151
147
  - `useSdk()` — raw bridge instance
152
148
  - `useNotify()` — fire-and-forget toast
153
149
  - `useSendToKernel()` — proxy a whitelisted message to the OS service worker
154
- - `useStandaloneDevLogout()` — clear standalone dev auth state and reload the page
155
150
 
156
151
  ### IndexedDB persistence
157
152
 
@@ -1,15 +1,10 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { type DevAuthProxyOptions } from './dev-proxy.js';
2
3
  type VitePlugin = {
3
4
  name: string;
4
5
  apply?: 'serve' | 'build';
5
6
  enforce?: 'pre' | 'post';
6
7
  configureServer?: (server: ViteDevServerLike) => void;
7
- transformIndexHtml?: () => Array<{
8
- tag: string;
9
- attrs?: Record<string, string>;
10
- children?: string;
11
- injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend';
12
- }> | void;
13
8
  };
14
9
  interface ViteDevServerLike {
15
10
  config: {
@@ -28,7 +23,8 @@ interface ViteDevServerLike {
28
23
  }
29
24
  export interface BardiocDevAuthOptions {
30
25
  appName: string;
31
- sessionStorageKey?: string;
26
+ /** Optional explicit proxy options. If omitted, DEV_SECRET and API_BASE_URL are read from env. */
27
+ proxy?: DevAuthProxyOptions;
32
28
  }
33
29
  export declare function bardiocDevAuth(options: BardiocDevAuthOptions): VitePlugin;
34
30
  export {};
@@ -1,221 +1,79 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
- import { forwardDevAuthProxyRequest, readJsonBody, sendJson, } from './dev-auth-proxy-core.js';
3
+ import { createDevAuthProxyHandler } from './dev-proxy.js';
4
4
  import { readDotEnvFile } from './dot-env.js';
5
- function resolveSessionStorageKey(options) {
6
- return options.sessionStorageKey ?? `bardioc.dev.auth.${options.appName}`;
7
- }
8
5
  function readRuntimeEnv(server) {
9
6
  const envDir = server.config.envDir ?? server.config.root;
10
7
  const mode = server.config.mode?.trim();
11
- const envFileNames = [
8
+ const env = {};
9
+ for (const fileName of [
12
10
  '.env',
13
11
  '.env.local',
14
12
  mode ? `.env.${mode}` : null,
15
13
  mode ? `.env.${mode}.local` : null,
16
- ];
17
- const env = {};
18
- for (const fileName of envFileNames) {
19
- if (!fileName) {
14
+ ]) {
15
+ if (!fileName)
20
16
  continue;
21
- }
22
17
  const filePath = resolve(envDir, fileName);
23
- if (!existsSync(filePath)) {
24
- continue;
18
+ if (existsSync(filePath)) {
19
+ Object.assign(env, readDotEnvFile(filePath));
25
20
  }
26
- Object.assign(env, readDotEnvFile(filePath));
27
21
  }
28
22
  for (const [key, value] of Object.entries(process.env)) {
29
23
  if (typeof value === 'string') {
30
24
  env[key] = value;
31
25
  }
32
26
  }
33
- return env;
34
- }
35
- function readDevAuthEnv(server, options) {
36
- const env = readRuntimeEnv(server);
37
- const hostUrl = env.DEV_AUTH_HOST_URL?.trim().replace(/\/$/, '');
38
- const devSessionKey = env.DEV_SESSION_KEY?.trim();
39
- const exchangeAppId = env.VITE_APP_ID?.trim();
40
- if (!hostUrl || !devSessionKey || !exchangeAppId) {
41
- return null;
42
- }
43
27
  return {
44
- exchangeAppId,
45
- hostUrl,
46
- devSessionKey,
47
- sessionStorageKey: resolveSessionStorageKey(options),
48
- };
49
- }
50
- function getRequestPathname(req) {
51
- const originalUrl = req.__bardiocOriginalUrl;
52
- return new URL(originalUrl ?? req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)
53
- .pathname;
54
- }
55
- function getBasePath(req, routePath) {
56
- const pathname = getRequestPathname(req);
57
- if (pathname.endsWith(routePath)) {
58
- const basePath = pathname.slice(0, -routePath.length);
59
- return basePath || '/';
60
- }
61
- return '/';
62
- }
63
- function buildAppPath(basePath, path) {
64
- const normalizedBase = basePath === '/' ? '' : basePath.replace(/\/$/, '');
65
- const normalizedPath = path.startsWith('/') ? path : `/${path}`;
66
- return `${normalizedBase}${normalizedPath}`;
67
- }
68
- function sendHtml(res, status, html) {
69
- res.statusCode = status;
70
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
71
- res.setHeader('Cache-Control', 'no-store');
72
- res.end(html);
73
- }
74
- function renderLoginHtml(session, basePath, sessionStorageKey) {
75
- const payload = JSON.stringify(JSON.stringify(session))
76
- .replace(/</g, '\\u003c')
77
- .replace(/>/g, '\\u003e')
78
- .replace(/&/g, '\\u0026');
79
- const redirectPath = buildAppPath(basePath, '/');
80
- return `<!doctype html>
81
- <html lang="en">
82
- <head>
83
- <meta charset="utf-8" />
84
- <meta name="viewport" content="width=device-width, initial-scale=1" />
85
- <title>Signing in...</title>
86
- </head>
87
- <body>
88
- <script>
89
- window.localStorage.removeItem('${sessionStorageKey.replace('bardioc.dev.auth.', '')}.dev.auth');
90
- window.localStorage.setItem('${sessionStorageKey}', ${payload});
91
- window.location.replace(${JSON.stringify(redirectPath)});
92
- </script>
93
- </body>
94
- </html>`;
95
- }
96
- function createLoginHandler(env) {
97
- return async (req, res) => {
98
- const basePath = getBasePath(req, '/login');
99
- sendHtml(res, 200, renderLoginHtml({
100
- sessionKey: env.devSessionKey,
101
- hostUrl: env.hostUrl,
102
- scopeId: null,
103
- }, basePath, env.sessionStorageKey));
104
- };
105
- }
106
- function createProxyHandler(options) {
107
- return async (req, res) => {
108
- const payload = await readJsonBody(req);
109
- if (!payload?.path) {
110
- sendJson(res, 400, { error: 'invalid_payload' });
111
- return;
112
- }
113
- try {
114
- const transportResponse = await forwardDevAuthProxyRequest(payload, {
115
- appId: options.appId,
116
- hostUrl: options.env.hostUrl,
117
- devSessionKey: options.env.devSessionKey,
118
- });
119
- const responseText = await transportResponse.text();
120
- res.statusCode = transportResponse.status;
121
- const contentType = transportResponse.headers.get('content-type');
122
- if (contentType) {
123
- res.setHeader('Content-Type', contentType);
124
- }
125
- res.setHeader('Cache-Control', 'no-store');
126
- res.end(responseText);
127
- }
128
- catch (error) {
129
- if (error instanceof Error && error.message === 'missing_dev_session') {
130
- sendJson(res, 400, { error: 'missing_dev_session' });
131
- return;
132
- }
133
- sendJson(res, 502, { error: 'host_transport_failed' });
134
- }
135
- };
136
- }
137
- function createDevAuthRouter(options) {
138
- const handleLogin = createLoginHandler(options.env);
139
- const handleProxy = createProxyHandler(options);
140
- const handlers = {
141
- 'GET /login': handleLogin,
142
- 'POST /proxy': handleProxy,
143
- };
144
- return async (req, res) => {
145
- const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
146
- const key = `${req.method ?? 'GET'} ${url.pathname}`;
147
- const handler = handlers[key];
148
- if (!handler) {
149
- sendJson(res, 404, { error: 'not_found' });
150
- return;
151
- }
152
- await handler(req, res);
28
+ DEV_SECRET: env.DEV_SECRET?.trim(),
29
+ API_BASE_URL: env.API_BASE_URL?.trim().replace(/\/$/, ''),
153
30
  };
154
31
  }
155
32
  export function bardiocDevAuth(options) {
156
- let runtimeConfig = null;
33
+ let proxyOptions = options.proxy ?? null;
157
34
  return {
158
35
  name: 'bardioc-dev-auth',
159
36
  apply: 'serve',
160
37
  enforce: 'pre',
161
38
  configureServer(server) {
162
- runtimeConfig = readDevAuthEnv(server, options);
163
- if (!runtimeConfig) {
164
- server.config.logger.warn(`[bardioc-dev-auth] DEV_AUTH_HOST_URL + DEV_SESSION_KEY, plus VITE_APP_ID, are required in env files — dev auth disabled for appName="${options.appName}".`);
165
- return;
39
+ if (!proxyOptions) {
40
+ const env = readRuntimeEnv(server);
41
+ if (!env.DEV_SECRET || !env.API_BASE_URL) {
42
+ server.config.logger.warn(`[bardioc-dev-auth] DEV_SECRET and API_BASE_URL are required for dev auth — disabled for appName="${options.appName}".`);
43
+ return;
44
+ }
45
+ proxyOptions = {
46
+ apiBaseUrl: env.API_BASE_URL,
47
+ devSecret: env.DEV_SECRET,
48
+ };
166
49
  }
167
- const env = {
168
- hostUrl: runtimeConfig.hostUrl,
169
- devSessionKey: runtimeConfig.devSessionKey,
170
- sessionStorageKey: runtimeConfig.sessionStorageKey,
171
- };
172
- const router = createDevAuthRouter({ appId: runtimeConfig.exchangeAppId, env });
173
- server.config.logger.info(`[bardioc-dev-auth] enabled for appName="${options.appName}" appId="${runtimeConfig.exchangeAppId}" → ${runtimeConfig.hostUrl}/api/transport`);
50
+ const handler = createDevAuthProxyHandler(proxyOptions);
174
51
  const basePath = server.config.base.endsWith('/')
175
52
  ? server.config.base.slice(0, -1)
176
53
  : server.config.base;
54
+ server.config.logger.info(`[bardioc-dev-auth] proxying /rest/* → ${proxyOptions.apiBaseUrl} for appName="${options.appName}"`);
177
55
  server.middlewares.use(async (req, res, next) => {
178
56
  const originalUrl = req.url ?? '';
179
- const devAuthReq = req;
180
57
  const parsed = new URL(originalUrl, 'http://localhost');
181
58
  const pathname = parsed.pathname;
182
59
  const routePath = pathname.startsWith(basePath)
183
60
  ? pathname.slice(basePath.length) || '/'
184
61
  : pathname;
185
- const shouldHandle = routePath === '/login' || routePath === '/proxy';
186
- if (shouldHandle) {
187
- try {
188
- devAuthReq.__bardiocOriginalUrl = originalUrl;
189
- req.url = `${routePath}${parsed.search}`;
190
- await router(req, res);
191
- }
192
- catch (error) {
193
- next(error);
194
- }
195
- finally {
196
- req.url = originalUrl;
197
- delete devAuthReq.__bardiocOriginalUrl;
198
- }
199
- }
200
- else {
62
+ if (!routePath.startsWith('/rest/')) {
201
63
  next();
64
+ return;
65
+ }
66
+ try {
67
+ req.url = `${routePath}${parsed.search}`;
68
+ await handler(req, res);
69
+ }
70
+ catch (error) {
71
+ next(error);
72
+ }
73
+ finally {
74
+ req.url = originalUrl;
202
75
  }
203
76
  });
204
77
  },
205
- transformIndexHtml() {
206
- if (!runtimeConfig) {
207
- return;
208
- }
209
- return [
210
- {
211
- tag: 'script',
212
- injectTo: 'head',
213
- children: `window.__BARDIOC_DEV_AUTH__ = ${JSON.stringify({
214
- hostUrl: runtimeConfig.hostUrl,
215
- sessionKey: runtimeConfig.devSessionKey,
216
- })};`,
217
- },
218
- ];
219
- },
220
78
  };
221
79
  }
@@ -1,8 +1,17 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
2
  export interface DevAuthProxyOptions {
3
- appId: string;
4
- hostUrl: string;
5
- devSessionKey: string;
3
+ /** Upstream BFF base URL (e.g. `https://your-bff-host`). */
4
+ apiBaseUrl: string;
5
+ /** Dev secret copied from the backend. */
6
+ devSecret: string;
7
+ /** Optional CORS origin (e.g. `*`). */
6
8
  allowOrigin?: string;
7
9
  }
10
+ interface SendJsonOptions {
11
+ allowOrigin?: string;
12
+ }
13
+ export declare function readRequestBody(req: IncomingMessage): Promise<Buffer>;
14
+ export declare function applyCors(res: ServerResponse, allowOrigin?: string): void;
15
+ export declare function sendJson(res: ServerResponse, status: number, body: unknown, options?: SendJsonOptions): void;
8
16
  export declare function createDevAuthProxyHandler(options: DevAuthProxyOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
17
+ export {};
package/dist/dev-proxy.js CHANGED
@@ -1,40 +1,99 @@
1
- import { applyCors, forwardDevAuthProxyRequest, readJsonBody, sendJson, } from './dev-auth-proxy-core.js';
1
+ export async function readRequestBody(req) {
2
+ return new Promise((resolve, reject) => {
3
+ const chunks = [];
4
+ req.on('data', (chunk) => chunks.push(chunk));
5
+ req.on('end', () => resolve(Buffer.concat(chunks)));
6
+ req.on('error', reject);
7
+ });
8
+ }
9
+ export function applyCors(res, allowOrigin) {
10
+ if (!allowOrigin) {
11
+ return;
12
+ }
13
+ res.setHeader('Access-Control-Allow-Origin', allowOrigin);
14
+ res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
15
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Scope-ID');
16
+ }
17
+ export function sendJson(res, status, body, options = {}) {
18
+ applyCors(res, options.allowOrigin);
19
+ res.statusCode = status;
20
+ res.setHeader('Content-Type', 'application/json');
21
+ res.setHeader('Cache-Control', 'no-store');
22
+ res.end(JSON.stringify(body));
23
+ }
24
+ function normalizeApiBaseUrl(value) {
25
+ return value.trim().replace(/\/$/, '');
26
+ }
27
+ function addDevSecretHeader(req, headers, devSecret) {
28
+ const url = req.url ?? '/';
29
+ if (url.startsWith('/rest/') && !url.startsWith('/rest/auth/local-dev-secret')) {
30
+ headers.set('X-Dev-Secret', devSecret);
31
+ }
32
+ return headers;
33
+ }
2
34
  export function createDevAuthProxyHandler(options) {
35
+ const apiBaseUrl = normalizeApiBaseUrl(options.apiBaseUrl);
36
+ const { devSecret, allowOrigin } = options;
3
37
  return async (req, res) => {
4
- const pathname = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`).pathname;
38
+ const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
5
39
  if (req.method === 'OPTIONS') {
6
- applyCors(res, options.allowOrigin);
40
+ applyCors(res, allowOrigin);
7
41
  res.statusCode = 204;
8
42
  res.end();
9
43
  return;
10
44
  }
11
- if (req.method !== 'POST' || pathname !== '/proxy') {
12
- sendJson(res, 404, { error: 'not_found' }, { allowOrigin: options.allowOrigin });
45
+ if (!url.pathname.startsWith('/rest/')) {
46
+ sendJson(res, 404, { error: 'not_found' }, { allowOrigin });
13
47
  return;
14
48
  }
15
- const payload = await readJsonBody(req);
16
- if (!payload?.path) {
17
- sendJson(res, 400, { error: 'invalid_payload' }, { allowOrigin: options.allowOrigin });
49
+ const upstreamPath = url.pathname.slice('/rest'.length) + url.search;
50
+ const upstreamUrl = `${apiBaseUrl}${upstreamPath}`;
51
+ const headers = new Headers();
52
+ const contentType = req.headers['content-type'];
53
+ if (contentType) {
54
+ headers.set('Content-Type', contentType);
55
+ }
56
+ addDevSecretHeader(req, headers, devSecret);
57
+ // Forward a few useful headers from the browser.
58
+ const forwardedHeaders = ['accept', 'accept-language', 'x-scope-id'];
59
+ for (const name of forwardedHeaders) {
60
+ const value = req.headers[name];
61
+ if (typeof value === 'string') {
62
+ headers.set(name, value);
63
+ }
64
+ else if (Array.isArray(value)) {
65
+ for (const v of value) {
66
+ headers.append(name, v);
67
+ }
68
+ }
69
+ }
70
+ let body;
71
+ try {
72
+ body = await readRequestBody(req);
73
+ }
74
+ catch {
75
+ sendJson(res, 400, { error: 'invalid_body' }, { allowOrigin });
18
76
  return;
19
77
  }
20
78
  try {
21
- const transportResponse = await forwardDevAuthProxyRequest(payload, options);
22
- const responseText = await transportResponse.text();
23
- applyCors(res, options.allowOrigin);
24
- res.statusCode = transportResponse.status;
79
+ const upstreamRes = await fetch(upstreamUrl, {
80
+ method: req.method ?? 'GET',
81
+ headers,
82
+ body: body.length > 0 ? new Uint8Array(body) : undefined,
83
+ cache: 'no-store',
84
+ });
85
+ applyCors(res, allowOrigin);
86
+ res.statusCode = upstreamRes.status;
25
87
  res.setHeader('Cache-Control', 'no-store');
26
- const contentType = transportResponse.headers.get('content-type');
27
- if (contentType) {
28
- res.setHeader('Content-Type', contentType);
88
+ const upstreamContentType = upstreamRes.headers.get('content-type');
89
+ if (upstreamContentType) {
90
+ res.setHeader('Content-Type', upstreamContentType);
29
91
  }
30
- res.end(responseText);
92
+ const responseBody = Buffer.from(await upstreamRes.arrayBuffer());
93
+ res.end(responseBody);
31
94
  }
32
- catch (error) {
33
- if (error instanceof Error && error.message === 'missing_dev_session') {
34
- sendJson(res, 400, { error: 'missing_dev_session' }, { allowOrigin: options.allowOrigin });
35
- return;
36
- }
37
- sendJson(res, 502, { error: 'host_transport_failed' }, { allowOrigin: options.allowOrigin });
95
+ catch {
96
+ sendJson(res, 502, { error: 'upstream_unavailable' }, { allowOrigin });
38
97
  }
39
98
  };
40
99
  }
File without changes
package/dist/dev.d.ts CHANGED
@@ -1,33 +1,8 @@
1
- export interface InjectedDevAuthConfig {
2
- hostUrl?: string;
3
- sessionKey?: string;
4
- proxyUrl?: string;
5
- }
6
- export interface DevAuthSession {
7
- sessionKey?: string;
8
- hostUrl?: string;
9
- scopeId: string | null;
10
- }
11
- export interface DevAuthStorageOptions {
12
- appId: string;
13
- appName?: string;
14
- storageKey?: string;
15
- }
16
- export interface EnsureDevAuthSessionOptions extends DevAuthStorageOptions {
17
- baseUrl?: string;
18
- loginPath?: string;
19
- redirect?: (url: string) => void;
20
- }
21
- export interface DevBridgeOptions extends DevAuthStorageOptions {
1
+ export interface DevBridgeOptions {
2
+ /** Base URL prepended to SDK transport paths. Defaults to `/rest`. */
3
+ apiBaseUrl?: string;
22
4
  debug?: boolean;
23
- baseUrl?: string;
24
- proxyPath?: string;
25
5
  }
26
- export declare function getInjectedDevAuthConfig(): InjectedDevAuthConfig | null;
27
6
  export declare function isInsideIframe(): boolean;
28
7
  export declare function isDevStandalone(): boolean;
29
- export declare function getDevSession(options: DevAuthStorageOptions): DevAuthSession | null;
30
- export declare function getDevScopeId(options: DevAuthStorageOptions): string | null;
31
- export declare function clearDevAuthSession(options: DevAuthStorageOptions): void;
32
- export declare function ensureDevAuthSession(options: EnsureDevAuthSessionOptions): Promise<void>;
33
- export declare function installDevBridge(options: DevBridgeOptions): () => void;
8
+ export declare function installDevBridge(options?: DevBridgeOptions): () => void;
package/dist/dev.js CHANGED
@@ -1,47 +1,6 @@
1
1
  import { SDK_MSG } from './protocol.js';
2
2
  function getBaseUrl(baseUrl) {
3
- const metaEnv = import.meta.env;
4
- return baseUrl ?? metaEnv?.BASE_URL ?? '/';
5
- }
6
- function resolveStorageKey(options) {
7
- return options.storageKey ?? `bardioc.dev.auth.${options.appName ?? options.appId}`;
8
- }
9
- function writeDevSession(storageKey, session) {
10
- localStorage.setItem(storageKey, JSON.stringify(session));
11
- }
12
- export function getInjectedDevAuthConfig() {
13
- const config = globalThis.__BARDIOC_DEV_AUTH__;
14
- if (!config?.hostUrl || !config?.sessionKey) {
15
- return null;
16
- }
17
- return {
18
- hostUrl: config.hostUrl,
19
- sessionKey: config.sessionKey,
20
- proxyUrl: typeof config.proxyUrl === 'string' ? config.proxyUrl : undefined,
21
- };
22
- }
23
- function readStoredSession(storageKey) {
24
- const raw = localStorage.getItem(storageKey);
25
- if (!raw) {
26
- return null;
27
- }
28
- try {
29
- const parsed = JSON.parse(raw);
30
- const hasHostSession = typeof parsed.sessionKey === 'string' && typeof parsed.hostUrl === 'string';
31
- if (!hasHostSession) {
32
- localStorage.removeItem(storageKey);
33
- return null;
34
- }
35
- return {
36
- sessionKey: typeof parsed.sessionKey === 'string' ? parsed.sessionKey : undefined,
37
- hostUrl: typeof parsed.hostUrl === 'string' ? parsed.hostUrl : undefined,
38
- scopeId: typeof parsed.scopeId === 'string' ? parsed.scopeId : null,
39
- };
40
- }
41
- catch {
42
- localStorage.removeItem(storageKey);
43
- return null;
44
- }
3
+ return baseUrl ?? '/rest';
45
4
  }
46
5
  export function isInsideIframe() {
47
6
  const currentWindow = globalThis;
@@ -51,57 +10,16 @@ export function isDevStandalone() {
51
10
  const metaEnv = import.meta.env;
52
11
  return Boolean(metaEnv?.DEV) && !isInsideIframe();
53
12
  }
54
- export function getDevSession(options) {
55
- return readStoredSession(resolveStorageKey(options));
56
- }
57
- export function getDevScopeId(options) {
58
- return getDevSession(options)?.scopeId ?? null;
59
- }
60
- export function clearDevAuthSession(options) {
61
- localStorage.removeItem(resolveStorageKey(options));
62
- }
63
- export async function ensureDevAuthSession(options) {
64
- const storageKey = resolveStorageKey(options);
65
- const url = new URL(window.location.href);
66
- const authError = url.searchParams.get('auth_error');
67
- if (authError) {
68
- console.error('[dev-auth] Authentication failed:', authError);
69
- url.searchParams.delete('auth_error');
70
- window.history.replaceState({}, '', url.toString());
71
- throw new Error(`Authentication failed: ${authError}`);
72
- }
73
- const injectedConfig = getInjectedDevAuthConfig();
74
- if (injectedConfig) {
75
- const storedSession = readStoredSession(storageKey);
76
- if (storedSession?.hostUrl !== injectedConfig.hostUrl ||
77
- storedSession?.sessionKey !== injectedConfig.sessionKey) {
78
- writeDevSession(storageKey, {
79
- hostUrl: injectedConfig.hostUrl,
80
- sessionKey: injectedConfig.sessionKey,
81
- scopeId: null,
82
- });
83
- }
84
- return;
85
- }
86
- const session = readStoredSession(storageKey);
87
- if (session?.sessionKey) {
88
- return;
13
+ function shouldForwardScope(policy, scopeId) {
14
+ if (policy === 'required') {
15
+ return true;
89
16
  }
90
- const loginPath = options.loginPath ?? 'login';
91
- const loginUrl = new URL(loginPath, window.location.origin + getBaseUrl(options.baseUrl)).toString();
92
- console.info('[dev-auth] redirecting browser to login', {
93
- currentUrl: url.toString(),
94
- baseUrl: getBaseUrl(options.baseUrl),
95
- loginUrl,
96
- });
97
- (options.redirect ?? ((nextUrl) => window.location.replace(nextUrl)))(loginUrl);
98
- await new Promise(() => { });
17
+ return policy === 'optional' && scopeId !== undefined;
99
18
  }
100
- export function installDevBridge(options) {
101
- const { appId, debug = false } = options;
19
+ export function installDevBridge(options = {}) {
20
+ const apiBaseUrl = getBaseUrl(options.apiBaseUrl);
21
+ const debug = options.debug ?? false;
102
22
  const origin = window.location.origin;
103
- const proxyUrl = new URL(options.proxyPath ?? 'proxy', window.location.origin + getBaseUrl(options.baseUrl)).toString();
104
- const readSession = () => getDevSession(options);
105
23
  const log = (...args) => {
106
24
  if (debug) {
107
25
  console.log('[bardioc-dev-bridge]', ...args);
@@ -117,71 +35,48 @@ export function installDevBridge(options) {
117
35
  window.postMessage({ type: SDK_MSG.INIT_ACK, id, payload }, origin);
118
36
  }
119
37
  async function handleTransport(payload) {
120
- const session = readSession();
121
- if (!session?.sessionKey) {
122
- throw new Error('No auth session available');
38
+ const headers = new Headers();
39
+ const body = payload.body == null
40
+ ? undefined
41
+ : payload.contentType === 'application/json'
42
+ ? JSON.stringify(payload.body)
43
+ : payload.body;
44
+ if (body && payload.contentType) {
45
+ headers.set('Content-Type', payload.contentType);
46
+ }
47
+ if (shouldForwardScope(payload.scopePolicy, payload.scopeId)) {
48
+ headers.set('X-Scope-ID', payload.scopeId ?? '');
123
49
  }
124
- const shouldForwardScope = payload.scopePolicy === 'optional' || payload.scopePolicy === 'required';
125
- const forwardedScopeId = shouldForwardScope
126
- ? (payload.scopeId ?? session.scopeId ?? null)
127
- : null;
128
- console.info('[bardioc-dev-bridge][1/2] sending dev session to local proxy', {
129
- proxyUrl,
130
- appId,
131
- path: payload.path,
50
+ const url = `${apiBaseUrl}${payload.path}`;
51
+ log('[transport]', payload.method ?? 'GET', url);
52
+ const response = await fetch(url, {
132
53
  method: payload.method ?? 'GET',
133
- scopePolicy: payload.scopePolicy ?? 'none',
134
- forwardedScopeId,
135
- hasDevSessionKey: !!session.sessionKey,
136
- nextStep: 'local_proxy_relays_to_host_transport',
137
- });
138
- const response = await fetch(proxyUrl, {
139
- method: 'POST',
140
- headers: { 'Content-Type': 'application/json' },
141
- body: JSON.stringify({
142
- sessionKey: session.sessionKey,
143
- hostUrl: session.hostUrl,
144
- scopeId: forwardedScopeId,
145
- path: payload.path,
146
- method: payload.method ?? 'GET',
147
- body: payload.body,
148
- contentType: payload.contentType,
149
- scopePolicy: payload.scopePolicy,
150
- }),
54
+ headers,
55
+ body,
56
+ credentials: 'include',
151
57
  cache: 'no-store',
152
58
  });
59
+ const contentType = response.headers.get('content-type') ?? '';
153
60
  const text = await response.text();
154
- let data = null;
155
- if (text) {
156
- try {
157
- data = JSON.parse(text);
158
- }
159
- catch {
160
- data = text;
161
- }
162
- }
163
61
  if (!response.ok) {
164
- console.error('[bardioc-dev-bridge] proxy request failed', {
165
- proxyUrl,
166
- path: payload.path,
167
- method: payload.method ?? 'GET',
168
- status: response.status,
169
- failureStage: response.headers.get('X-Bardioc-Dev-Auth-Stage'),
170
- responsePayload: data,
171
- });
62
+ const responsePayload = contentType.includes('application/json')
63
+ ? JSON.parse(text)
64
+ : text;
172
65
  const err = new Error(`Transport ${payload.path} failed: ${response.status}`);
173
- Object.assign(err, { status: response.status, responsePayload: data });
66
+ Object.assign(err, { status: response.status, responsePayload });
174
67
  throw err;
175
68
  }
176
- return data;
69
+ if (!text) {
70
+ return null;
71
+ }
72
+ return contentType.includes('application/json') ? JSON.parse(text) : text;
177
73
  }
178
74
  async function handleKernelProxy(payload) {
179
75
  if (payload.kernelType === 'PING') {
180
76
  return { pong: true };
181
77
  }
182
78
  if (payload.kernelType === 'GET_SESSION_STATE') {
183
- const session = readSession();
184
- return { authenticated: !!session?.sessionKey };
79
+ return { authenticated: true };
185
80
  }
186
81
  throw new Error(`Kernel message ${payload.kernelType} not supported in dev bridge`);
187
82
  }
@@ -200,7 +95,7 @@ export function installDevBridge(options) {
200
95
  try {
201
96
  switch (data.type) {
202
97
  case SDK_MSG.INIT:
203
- ack(data.id, { appId, instanceId: null });
98
+ ack(data.id, { appId: null, instanceId: null });
204
99
  return;
205
100
  case SDK_MSG.NOTIFY:
206
101
  return;
@@ -1,4 +1,3 @@
1
- import { getInjectedDevAuthConfig } from './dev.js';
2
1
  import { SDK_MSG } from './protocol.js';
3
2
  import { GraphTransport } from './transports/graph-transport.js';
4
3
  import { OsTransport } from './transports/os-transport.js';
@@ -36,10 +35,6 @@ export function createHostBridge(config) {
36
35
  console.log('[app-sdk]', ...args);
37
36
  }
38
37
  log('createHostBridge', { appId: hintAppId, targetOrigin, isInsideIframe });
39
- const standaloneDevAuthActive = !isInsideIframe && !!getInjectedDevAuthConfig();
40
- if (!isInsideIframe && !standaloneDevAuthActive) {
41
- console.warn('[app-sdk] NOT inside an iframe — bridge will not work standalone.');
42
- }
43
38
  let _connected = false;
44
39
  let _context = null;
45
40
  let _destroyed = false;
package/dist/react.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { type ReactNode } from 'react';
2
- import { type DevAuthStorageOptions } from './dev.js';
3
2
  import type { GraphEvent, HostBridge, HostBridgeConfig, NotifyLevel, OsConfig, SdkCommandDefinition, SdkCommandHandler, SdkMenuRoot } from './types.js';
4
3
  export interface AppSdkProviderProps extends HostBridgeConfig {
5
4
  children: ReactNode;
@@ -30,5 +29,3 @@ export declare function useOsConfig(): OsConfig | null;
30
29
  export declare function useGraphEvents(options: {
31
30
  types: string[];
32
31
  }, handler: (event: GraphEvent) => void, deps?: React.DependencyList): void;
33
- /** Returns a stable callback that clears standalone dev auth state and reloads the page. */
34
- export declare function useStandaloneDevLogout(options: DevAuthStorageOptions): () => void;
package/dist/react.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
3
- import { clearDevAuthSession } from './dev.js';
4
3
  import { createHostBridge } from './host-bridge.js';
5
4
  const SdkContext = createContext(null);
6
5
  const SYSTEM_SIZE_CLASS_NAMES = ['text-sm', 'text-base', 'text-lg', 'text-xl'];
@@ -215,10 +214,3 @@ export function useGraphEvents(options, handler, deps = []) {
215
214
  // eslint-disable-next-line react-hooks/exhaustive-deps
216
215
  }, [bridge, typesKey, ...deps]);
217
216
  }
218
- /** Returns a stable callback that clears standalone dev auth state and reloads the page. */
219
- export function useStandaloneDevLogout(options) {
220
- return useCallback(() => {
221
- clearDevAuthSession(options);
222
- window.location.reload();
223
- }, [options]);
224
- }
package/dist/vite.js CHANGED
@@ -3,7 +3,6 @@ import { execSync, spawn } from 'node:child_process';
3
3
  import { existsSync, readFileSync } from 'node:fs';
4
4
  import { resolve, sep } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { readDotEnvValue } from './dot-env.js';
7
6
  import { MANIFEST_FILENAME, validateManifest } from './manifest.js';
8
7
  export { bardiocDevAuth } from './dev-auth-vite.js';
9
8
  function hasCommand(cmd) {
@@ -91,8 +90,6 @@ function devProxy(opts) {
91
90
  }
92
91
  server.httpServer?.on('listening', async () => {
93
92
  const manifestPath = resolve(process.cwd(), 'public', MANIFEST_FILENAME);
94
- const envPath = resolve(process.cwd(), '.env');
95
- const envAppId = existsSync(envPath) ? readDotEnvValue(envPath, 'VITE_APP_ID') : null;
96
93
  if (!existsSync(manifestPath)) {
97
94
  console.warn(`[bardioc] public/${MANIFEST_FILENAME} not found`);
98
95
  return;
@@ -114,14 +111,12 @@ function devProxy(opts) {
114
111
  }
115
112
  console.log(`[bardioc] Use this live URL in the host App Store app configuration:`);
116
113
  console.log(`[bardioc] App Name: ${manifestId}`);
117
- console.log(`[bardioc] App ID: ${envAppId || '<from .env VITE_APP_ID>'}`);
118
114
  console.log(`[bardioc] Live URL: https://${tunnelHost}`);
119
115
  }
120
116
  else {
121
117
  console.log(`[bardioc] Dev server ready: http://localhost:${port}`);
122
- console.log(`[bardioc] For standalone host-backed auth, open this app in the host App Store and use "Copy Dev Session".`);
118
+ console.log(`[bardioc] For standalone dev auth, set DEV_SECRET and API_BASE_URL in .env.`);
123
119
  console.log(`[bardioc] App Name: ${manifestId}`);
124
- console.log(`[bardioc] App ID: ${envAppId || '<from .env VITE_APP_ID>'}`);
125
120
  console.log(`[bardioc] URL: http://localhost:${port}`);
126
121
  }
127
122
  });
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@bardioc/app-sdk",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "SDK for building apps for the Bardioc OS",
5
5
  "license": "MIT",
6
6
  "private": false,
7
7
  "type": "module",
8
- "bin": {
9
- "bardioc-dev-session": "./dist/dev-session-cli.js"
10
- },
11
8
  "publishConfig": {
12
9
  "registry": "https://registry.npmjs.org/",
13
10
  "access": "public"
@@ -44,10 +41,6 @@
44
41
  "types": "./dist/dev-proxy.d.ts",
45
42
  "default": "./dist/dev-proxy.js"
46
43
  },
47
- "./dev-session-cli": {
48
- "types": "./dist/dev-session-cli.d.ts",
49
- "default": "./dist/dev-session-cli.js"
50
- },
51
44
  "./types": {
52
45
  "types": "./dist/types.d.ts"
53
46
  }