@cosmoops/connect-sdk 0.30.0 → 0.30.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,32 @@
3
3
  `@cosmoops/connect-sdk` follows semver, with the 0.x convention that **minor
4
4
  versions may carry breaking changes** and patch versions never do.
5
5
 
6
+ ## 0.30.1
7
+
8
+ `ConnectAppShell` was putting **462 KB** of markdown renderer on every page of
9
+ every application platform that mounts it — which is every page, because the
10
+ shell owns the layout. `streamdown` was a static import in `ConnectAskAi`, so
11
+ it was downloaded, parsed and evaluated on each route to render nothing until
12
+ somebody opened the assistant, which most page views never do. Measured on a
13
+ partner app: 48 of its 49 routes carried it, roughly a quarter of each route's
14
+ JavaScript.
15
+
16
+ It is now `React.lazy`, so the chunk is requested the first time there is
17
+ markdown to render and cached by React from then on. No API changed and no
18
+ host setup changed; `streamdown` stays a peer dependency of `/client` and is
19
+ still needed for formatted answers.
20
+
21
+ The Suspense fallback is the **raw text** rather than a spinner. An answer
22
+ streams in token by token, and unformatted prose is readable — so the first
23
+ words now appear *while* the renderer is being fetched instead of behind it,
24
+ and re-render as markdown when it lands. On a cold cache that makes the first
25
+ answer feel earlier than it did before, not later.
26
+
27
+ ### Changed
28
+
29
+ - `ConnectAskAi` loads `streamdown` on demand instead of at module scope.
30
+ Partner apps get the saving by upgrading; nothing in their code moves.
31
+
6
32
  ## 0.30.0
7
33
 
8
34
  `<ConnectAskAi>` forgot everything on reload. Connect's own panel has had
@@ -1,13 +1,68 @@
1
1
  "use client";
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
37
  exports.ConnectAskAi = ConnectAskAi;
5
38
  const jsx_runtime_1 = require("react/jsx-runtime");
6
39
  const react_1 = require("react");
7
40
  const react_2 = require("@heroui/react");
8
41
  const lucide_react_1 = require("lucide-react");
9
- const streamdown_1 = require("streamdown");
10
42
  const provider_js_1 = require("../provider.js");
43
+ /**
44
+ * The markdown renderer, fetched the first time there is markdown to render.
45
+ *
46
+ * `streamdown` is **462 KB** of the host's client bundle, and a static import
47
+ * here put all of it on every page of every application platform that mounts
48
+ * `ConnectAppShell` - which is every page, because the shell owns the layout.
49
+ * It rendered nothing until somebody opened the assistant, which most page
50
+ * views never do. Measured on one partner app: 48 of 49 routes carrying it,
51
+ * about a quarter of each route's JavaScript.
52
+ *
53
+ * `lazy` rather than a conditional `import()` in an effect: React caches the
54
+ * module promise, so the chunk is requested once no matter how many messages
55
+ * render, and the boundary below is what covers the gap.
56
+ *
57
+ * The fallback is the **raw text**, not a spinner. An answer streams in token
58
+ * by token, and unformatted prose is readable - so the first words appear
59
+ * while the renderer is still arriving rather than behind it. Once it lands,
60
+ * the same text re-renders as markdown.
61
+ */
62
+ const Streamdown = (0, react_1.lazy)(async () => {
63
+ const mod = await Promise.resolve().then(() => __importStar(require("streamdown")));
64
+ return { default: mod.Streamdown };
65
+ });
11
66
  const use_active_path_js_1 = require("../use-active-path.js");
12
67
  const ai_screens_js_1 = require("./ai-screens.js");
13
68
  const ai_widgets_js_1 = require("./ai-widgets.js");
@@ -267,11 +322,11 @@ function ConnectAskAi({ title = "Ask AI", assistantName = "Assistant", placehold
267
322
  */
268
323
  const resolvedPath = (0, use_active_path_js_1.useActivePath)(activePath);
269
324
  const currentScreen = (0, react_1.useMemo)(() => (resolvedPath ? (0, ai_screens_js_1.matchConnectScreen)(screens, resolvedPath) : null), [screens, resolvedPath]);
270
- const renderMarkdown = (0, react_1.useCallback)((text, isStreaming = false) => ((0, jsx_runtime_1.jsx)(streamdown_1.Streamdown, { className: "connect-ai-body", mode: isStreaming ? "streaming" : "static", isAnimating: isStreaming, animated: ANIMATED, caret: isStreaming ? "block" : undefined, plugins: markdownPlugins,
271
- // Retrieved documents cite real URLs; an interstitial on every one of
272
- // them turns a citation into a chore. The renderer still sanitises the
273
- // markup itself, which is the part that matters.
274
- linkSafety: LINK_SAFETY, children: text })), [markdownPlugins]);
325
+ const renderMarkdown = (0, react_1.useCallback)((text, isStreaming = false) => ((0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: (0, jsx_runtime_1.jsx)("div", { className: "connect-ai-body whitespace-pre-wrap", children: text }), children: (0, jsx_runtime_1.jsx)(Streamdown, { className: "connect-ai-body", mode: isStreaming ? "streaming" : "static", isAnimating: isStreaming, animated: ANIMATED, caret: isStreaming ? "block" : undefined, plugins: markdownPlugins,
326
+ // Retrieved documents cite real URLs; an interstitial on every one of
327
+ // them turns a citation into a chore. The renderer still sanitises the
328
+ // markup itself, which is the part that matters.
329
+ linkSafety: LINK_SAFETY, children: text }) })), [markdownPlugins]);
275
330
  const stop = (0, react_1.useCallback)(() => {
276
331
  abortRef.current?.abort();
277
332
  abortRef.current = null;
@@ -165,4 +165,4 @@ class ConnectClient {
165
165
  }
166
166
  exports.ConnectClient = ConnectClient;
167
167
  /** Kept in step with `package.json` by `sdk.test.ts`. */
168
- exports.SDK_VERSION = "0.30.0";
168
+ exports.SDK_VERSION = "0.30.1";
@@ -1,10 +1,32 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
4
4
  import { Button, Input, Label, TextField } from "@heroui/react";
5
5
  import { Brain, ChevronDown, History, Plus, Send, Sparkles, Square, X } from "lucide-react";
6
- import { Streamdown } from "streamdown";
7
6
  import { useConnect } from "../provider.js";
7
+ /**
8
+ * The markdown renderer, fetched the first time there is markdown to render.
9
+ *
10
+ * `streamdown` is **462 KB** of the host's client bundle, and a static import
11
+ * here put all of it on every page of every application platform that mounts
12
+ * `ConnectAppShell` - which is every page, because the shell owns the layout.
13
+ * It rendered nothing until somebody opened the assistant, which most page
14
+ * views never do. Measured on one partner app: 48 of 49 routes carrying it,
15
+ * about a quarter of each route's JavaScript.
16
+ *
17
+ * `lazy` rather than a conditional `import()` in an effect: React caches the
18
+ * module promise, so the chunk is requested once no matter how many messages
19
+ * render, and the boundary below is what covers the gap.
20
+ *
21
+ * The fallback is the **raw text**, not a spinner. An answer streams in token
22
+ * by token, and unformatted prose is readable - so the first words appear
23
+ * while the renderer is still arriving rather than behind it. Once it lands,
24
+ * the same text re-renders as markdown.
25
+ */
26
+ const Streamdown = lazy(async () => {
27
+ const mod = await import("streamdown");
28
+ return { default: mod.Streamdown };
29
+ });
8
30
  import { useActivePath } from "../use-active-path.js";
9
31
  import { matchConnectScreen } from "./ai-screens.js";
10
32
  import { AiActions, AiChart, AiDetails, AiMetrics, AiSources, AiSteps, AiTable, AiTimeline } from "./ai-widgets.js";
@@ -264,11 +286,11 @@ export function ConnectAskAi({ title = "Ask AI", assistantName = "Assistant", pl
264
286
  */
265
287
  const resolvedPath = useActivePath(activePath);
266
288
  const currentScreen = useMemo(() => (resolvedPath ? matchConnectScreen(screens, resolvedPath) : null), [screens, resolvedPath]);
267
- const renderMarkdown = useCallback((text, isStreaming = false) => (_jsx(Streamdown, { className: "connect-ai-body", mode: isStreaming ? "streaming" : "static", isAnimating: isStreaming, animated: ANIMATED, caret: isStreaming ? "block" : undefined, plugins: markdownPlugins,
268
- // Retrieved documents cite real URLs; an interstitial on every one of
269
- // them turns a citation into a chore. The renderer still sanitises the
270
- // markup itself, which is the part that matters.
271
- linkSafety: LINK_SAFETY, children: text })), [markdownPlugins]);
289
+ const renderMarkdown = useCallback((text, isStreaming = false) => (_jsx(Suspense, { fallback: _jsx("div", { className: "connect-ai-body whitespace-pre-wrap", children: text }), children: _jsx(Streamdown, { className: "connect-ai-body", mode: isStreaming ? "streaming" : "static", isAnimating: isStreaming, animated: ANIMATED, caret: isStreaming ? "block" : undefined, plugins: markdownPlugins,
290
+ // Retrieved documents cite real URLs; an interstitial on every one of
291
+ // them turns a citation into a chore. The renderer still sanitises the
292
+ // markup itself, which is the part that matters.
293
+ linkSafety: LINK_SAFETY, children: text }) })), [markdownPlugins]);
272
294
  const stop = useCallback(() => {
273
295
  abortRef.current?.abort();
274
296
  abortRef.current = null;
@@ -161,4 +161,4 @@ export class ConnectClient {
161
161
  }
162
162
  }
163
163
  /** Kept in step with `package.json` by `sdk.test.ts`. */
164
- export const SDK_VERSION = "0.30.0";
164
+ export const SDK_VERSION = "0.30.1";
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { type StreamdownProps } from "streamdown";
2
+ import type { StreamdownProps } from "streamdown";
3
3
  import type { ConnectScreen } from "../../types.js";
4
4
  export interface ConnectAskAiProps {
5
5
  /**
@@ -126,4 +126,4 @@ export declare class ConnectClient {
126
126
  constructor(options: ConnectClientOptions);
127
127
  }
128
128
  /** Kept in step with `package.json` by `sdk.test.ts`. */
129
- export declare const SDK_VERSION = "0.30.0";
129
+ export declare const SDK_VERSION = "0.30.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmoops/connect-sdk",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
4
4
  "description": "Official server-side SDK for the CosmoOps Connect platform API - Login with Connect, entitlements, billing and metering.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "commonjs",