@hosty-sdk/app 0.21.0 → 0.21.2

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
@@ -25,6 +25,13 @@ Minimal Next.js wiring:
25
25
  import { AppIdentityBridge } from "@hosty-sdk/app/react";
26
26
  // wrap protected client content: <AppIdentityBridge>{children}</AppIdentityBridge>
27
27
 
28
+ // Reuse an active probe that already returns the app's session fields:
29
+ // <AppIdentityBridge probePath="/api/auth/session" onSession={seedSessionCache}>
30
+ // {children}
31
+ // </AppIdentityBridge>
32
+ // seedSessionCache receives unknown JSON; validate app-specific fields before caching.
33
+ // The callback runs before children mount, only for a successful active probe.
34
+
28
35
  // app/api/auth/app-code/route.ts
29
36
  import { createAppCodeRouteHandler } from "@hosty-sdk/app/server";
30
37
  export const dynamic = "force-dynamic";
package/dist/install.d.ts CHANGED
@@ -83,7 +83,7 @@ export interface InstallationSource {
83
83
  description?: string | null;
84
84
  };
85
85
  };
86
- /** Personal source selection requires apps.sources as well as apps.install and Core review. */
86
+ /** Personal source selection requires apps.install plus apps.sources.full or sources.connections and Core review. */
87
87
  sourceConnections?: {
88
88
  manifestConnectionId?: string;
89
89
  gitConnectionId?: string;
package/dist/react.d.ts CHANGED
@@ -50,6 +50,8 @@ export interface AppIdentityBridgeProps {
50
50
  appCodePath?: string;
51
51
  /** Optional app-owned rendering, including loading and active content. Recovery remains SDK-owned. */
52
52
  renderState?: (state: AppIdentityBridgeState) => ReactNode;
53
+ /** Receives a successful active probe before children mount, so an app can seed its session cache. */
54
+ onSession?: (body: unknown) => void;
53
55
  children?: ReactNode;
54
56
  }
55
57
  /**
@@ -60,7 +62,7 @@ export interface AppIdentityBridgeProps {
60
62
  * redirect through Core `/open` when standalone, and cards only in fallback/terminal states
61
63
  * — never a login UI while recovery is still running.
62
64
  */
63
- export declare function AppIdentityBridge({ probePath, appCodePath, renderState, children, }?: AppIdentityBridgeProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
65
+ export declare function AppIdentityBridge({ probePath, appCodePath, renderState, onSession, children, }?: AppIdentityBridgeProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
64
66
  export interface HostThemeBridgeProps {
65
67
  /**
66
68
  * Whether the operating system decides the theme while no shell has spoken — `true` unless the
package/dist/react.js CHANGED
@@ -187,7 +187,7 @@ export function useLaunchMode() {
187
187
  * redirect through Core `/open` when standalone, and cards only in fallback/terminal states
188
188
  * — never a login UI while recovery is still running.
189
189
  */
190
- export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePath = "/api/auth/app-code", renderState, children, } = {}) {
190
+ export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePath = "/api/auth/app-code", renderState, onSession, children, } = {}) {
191
191
  const [ui, setUi] = useState({ kind: "recovering" });
192
192
  // A launch code is single-use. Strict Mode replays the effect after the URL is cleaned;
193
193
  // keep its exchange alive and let the replacement effect await the same response.
@@ -199,6 +199,8 @@ export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePat
199
199
  const upgradedAttempt = useRef(false);
200
200
  const [activity, setActivity] = useState(null);
201
201
  const exchangeRef = useRef(null);
202
+ const onSessionRef = useRef(onSession);
203
+ useEffect(() => { onSessionRef.current = onSession; }, [onSession]);
202
204
  useEffect(() => {
203
205
  let cancelled = false;
204
206
  const controller = new AbortController();
@@ -218,6 +220,7 @@ export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePat
218
220
  let protocol = null;
219
221
  let activityMetadata = null;
220
222
  let rejectionCode;
223
+ let sessionBody = null;
221
224
  const revision = appGrantRevision();
222
225
  try {
223
226
  const response = await appFetch(probePath, {
@@ -227,6 +230,9 @@ export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePat
227
230
  }, false);
228
231
  const body = await response.json().catch(() => null);
229
232
  status = readProbedSessionStatus(body);
233
+ if (status === "active" && !response.ok)
234
+ status = null;
235
+ sessionBody = body;
230
236
  const identityError = body;
231
237
  rejectionCode = identityError?.error?.code ?? identityError?.appSession?.error?.code;
232
238
  const recovery = readRecoveryParams(body);
@@ -289,6 +295,7 @@ export function AppIdentityBridge({ probePath = "/api/auth/identity", appCodePat
289
295
  });
290
296
  switch (action.kind) {
291
297
  case "none":
298
+ onSessionRef.current?.(sessionBody);
292
299
  appSessionActive();
293
300
  wasActive.current = true;
294
301
  setUi({ kind: "active" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hosty-sdk/app",
3
- "version": "0.21.0",
3
+ "version": "0.21.2",
4
4
  "description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
@@ -91,7 +91,7 @@
91
91
  "server-only": "^0.0.1"
92
92
  },
93
93
  "peerDependencies": {
94
- "next": "^16.3.6",
94
+ "next": "^16.3.8",
95
95
  "react": "^19.2.8"
96
96
  },
97
97
  "peerDependenciesMeta": {
@@ -103,14 +103,14 @@
103
103
  }
104
104
  },
105
105
  "devDependencies": {
106
- "@types/node": "^24.19.0",
106
+ "@types/node": "^24.19.1",
107
107
  "@types/react": "^19.3.0",
108
- "typescript": "^6",
109
- "vitest": "^5.0.2",
108
+ "@types/react-dom": "^19.3.0",
109
+ "jsdom": "^30.1.1",
110
110
  "react": "^19.3.0",
111
111
  "react-dom": "^19.3.0",
112
- "@types/react-dom": "^19.3.0",
113
- "jsdom": "^30.1.1"
112
+ "typescript": "^6",
113
+ "vitest": "^5.0.3"
114
114
  },
115
115
  "repository": {
116
116
  "type": "git",