@streamoid/agent 0.1.0

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,80 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ /** Workspace context, passed through whole — extra fields are preserved, never dropped. */
5
+ interface AgentWorkspace {
6
+ id: string;
7
+ name?: string;
8
+ role?: string;
9
+ /** ISO country code, if the host has it. */
10
+ country?: string;
11
+ /** Display currency, if the host has it. */
12
+ currency?: string;
13
+ locale?: string;
14
+ availableCredits?: number | null;
15
+ totalCredits?: number | null;
16
+ [key: string]: unknown;
17
+ }
18
+ interface AgentUser {
19
+ id: string;
20
+ email: string;
21
+ specialization?: string | null;
22
+ [key: string]: unknown;
23
+ }
24
+ interface AgentNotification {
25
+ title?: string;
26
+ body?: string;
27
+ }
28
+ interface AgentPanelAdapters {
29
+ auth: {
30
+ user: AgentUser;
31
+ };
32
+ workspace: {
33
+ current: AgentWorkspace;
34
+ };
35
+ theme: {
36
+ mode: "light" | "dark";
37
+ };
38
+ config: {
39
+ /** Copilot widget origin/base (Phase-1 transitional; removed when native). */
40
+ widgetUrl: string;
41
+ hide?: {
42
+ sidepane?: boolean;
43
+ newBrief?: boolean;
44
+ notifications?: boolean;
45
+ };
46
+ };
47
+ navigation: {
48
+ /** Conversation to load on (re)mount; subsequent navigation goes through the ref. */
49
+ seedChatId?: string | null;
50
+ /** Fired when the agent navigates to (or clears) a conversation. */
51
+ onNavigated: (chatId: string | null) => void;
52
+ /** Fired once the agent has booted and is accepting commands. */
53
+ onReady?: () => void;
54
+ };
55
+ notifications: {
56
+ onProcessingComplete: (payload?: AgentNotification) => void;
57
+ onAttentionNeeded: (payload?: AgentNotification) => void;
58
+ };
59
+ }
60
+ /** Imperative actions the host triggers (sidebar new/open, chat-not-found reload). */
61
+ interface AgentPanelHandle {
62
+ /** Start a new conversation. */
63
+ newChat: () => void;
64
+ /** Switch the displayed conversation. */
65
+ navigateTo: (chatId: string) => void;
66
+ /** Remount the panel, optionally seeding a conversation (null = home). */
67
+ reload: (seedChatId?: string | null) => void;
68
+ }
69
+ interface AgentPanelProps {
70
+ adapters: AgentPanelAdapters;
71
+ className?: string;
72
+ style?: CSSProperties;
73
+ /** Corner radius for the panel + loading overlay. */
74
+ borderRadius?: number | string;
75
+ /** Text shown while the agent boots. */
76
+ loadingLabel?: string;
77
+ }
78
+ declare const AgentPanel: react.ForwardRefExoticComponent<AgentPanelProps & react.RefAttributes<AgentPanelHandle>>;
79
+
80
+ export { type AgentNotification, AgentPanel, type AgentPanelAdapters, type AgentPanelHandle, type AgentPanelProps, type AgentUser, type AgentWorkspace };
@@ -0,0 +1,80 @@
1
+ import * as react from 'react';
2
+ import { CSSProperties } from 'react';
3
+
4
+ /** Workspace context, passed through whole — extra fields are preserved, never dropped. */
5
+ interface AgentWorkspace {
6
+ id: string;
7
+ name?: string;
8
+ role?: string;
9
+ /** ISO country code, if the host has it. */
10
+ country?: string;
11
+ /** Display currency, if the host has it. */
12
+ currency?: string;
13
+ locale?: string;
14
+ availableCredits?: number | null;
15
+ totalCredits?: number | null;
16
+ [key: string]: unknown;
17
+ }
18
+ interface AgentUser {
19
+ id: string;
20
+ email: string;
21
+ specialization?: string | null;
22
+ [key: string]: unknown;
23
+ }
24
+ interface AgentNotification {
25
+ title?: string;
26
+ body?: string;
27
+ }
28
+ interface AgentPanelAdapters {
29
+ auth: {
30
+ user: AgentUser;
31
+ };
32
+ workspace: {
33
+ current: AgentWorkspace;
34
+ };
35
+ theme: {
36
+ mode: "light" | "dark";
37
+ };
38
+ config: {
39
+ /** Copilot widget origin/base (Phase-1 transitional; removed when native). */
40
+ widgetUrl: string;
41
+ hide?: {
42
+ sidepane?: boolean;
43
+ newBrief?: boolean;
44
+ notifications?: boolean;
45
+ };
46
+ };
47
+ navigation: {
48
+ /** Conversation to load on (re)mount; subsequent navigation goes through the ref. */
49
+ seedChatId?: string | null;
50
+ /** Fired when the agent navigates to (or clears) a conversation. */
51
+ onNavigated: (chatId: string | null) => void;
52
+ /** Fired once the agent has booted and is accepting commands. */
53
+ onReady?: () => void;
54
+ };
55
+ notifications: {
56
+ onProcessingComplete: (payload?: AgentNotification) => void;
57
+ onAttentionNeeded: (payload?: AgentNotification) => void;
58
+ };
59
+ }
60
+ /** Imperative actions the host triggers (sidebar new/open, chat-not-found reload). */
61
+ interface AgentPanelHandle {
62
+ /** Start a new conversation. */
63
+ newChat: () => void;
64
+ /** Switch the displayed conversation. */
65
+ navigateTo: (chatId: string) => void;
66
+ /** Remount the panel, optionally seeding a conversation (null = home). */
67
+ reload: (seedChatId?: string | null) => void;
68
+ }
69
+ interface AgentPanelProps {
70
+ adapters: AgentPanelAdapters;
71
+ className?: string;
72
+ style?: CSSProperties;
73
+ /** Corner radius for the panel + loading overlay. */
74
+ borderRadius?: number | string;
75
+ /** Text shown while the agent boots. */
76
+ loadingLabel?: string;
77
+ }
78
+ declare const AgentPanel: react.ForwardRefExoticComponent<AgentPanelProps & react.RefAttributes<AgentPanelHandle>>;
79
+
80
+ export { type AgentNotification, AgentPanel, type AgentPanelAdapters, type AgentPanelHandle, type AgentPanelProps, type AgentUser, type AgentWorkspace };
package/dist/index.js ADDED
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AgentPanel: () => AgentPanel
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/AgentPanel.tsx
28
+ var import_react = require("react");
29
+ var import_jsx_runtime = require("react/jsx-runtime");
30
+ function normalizeChatId(value) {
31
+ const trimmed = value?.trim();
32
+ return trimmed ? trimmed : null;
33
+ }
34
+ function buildSrc(adapters, seedChatId) {
35
+ const { auth, workspace, theme, config } = adapters;
36
+ const base = config.widgetUrl.endsWith("/widget/") ? config.widgetUrl : `${config.widgetUrl.replace(/\/+$/, "")}/widget/`;
37
+ const url = new URL(base);
38
+ if (workspace.current.id) url.searchParams.set("workspaceId", workspace.current.id);
39
+ if (auth.user.id) url.searchParams.set("userId", auth.user.id);
40
+ if (auth.user.email) url.searchParams.set("email", auth.user.email);
41
+ if (auth.user.specialization != null) {
42
+ url.searchParams.set("userSpecialization", String(auth.user.specialization));
43
+ }
44
+ url.searchParams.set("theme", theme.mode);
45
+ if (typeof window !== "undefined") {
46
+ url.searchParams.set("parentOrigin", window.location.origin);
47
+ }
48
+ if (config.hide?.sidepane) url.searchParams.set("hideSidepane", "1");
49
+ if (config.hide?.newBrief) url.searchParams.set("hideNewBrief", "1");
50
+ if (config.hide?.notifications) url.searchParams.set("hideNotifications", "1");
51
+ return `${url.toString()}#${seedChatId ? `/chat/${seedChatId}` : "/"}`;
52
+ }
53
+ function isNotificationType(type) {
54
+ return type === "chat:processing-complete" || type === "chat:attention-needed";
55
+ }
56
+ var AgentPanel = (0, import_react.forwardRef)(
57
+ function AgentPanel2({ adapters, className, style, borderRadius, loadingLabel = "Loading\u2026" }, ref) {
58
+ const { auth, workspace, theme, config, navigation, notifications } = adapters;
59
+ const workspaceId = workspace.current.id;
60
+ const iframeRef = (0, import_react.useRef)(null);
61
+ const widgetOrigin = (0, import_react.useMemo)(() => {
62
+ try {
63
+ return new URL(config.widgetUrl).origin;
64
+ } catch {
65
+ return "*";
66
+ }
67
+ }, [config.widgetUrl]);
68
+ const [loaded, setLoaded] = (0, import_react.useState)(false);
69
+ const [ready, setReady] = (0, import_react.useState)(false);
70
+ const [reloadNonce, setReloadNonce] = (0, import_react.useState)(0);
71
+ const seedRef = (0, import_react.useRef)(normalizeChatId(navigation.seedChatId));
72
+ const mountKey = `${auth.user.id}|${auth.user.specialization ?? ""}|${reloadNonce}`;
73
+ const lastThemeRef = (0, import_react.useRef)(null);
74
+ const lastWorkspaceRef = (0, import_react.useRef)(null);
75
+ const pendingRef = (0, import_react.useRef)(null);
76
+ const srcRef = (0, import_react.useRef)({ key: "", src: "" });
77
+ if (srcRef.current.key !== mountKey) {
78
+ srcRef.current = { key: mountKey, src: buildSrc(adapters, seedRef.current) };
79
+ lastThemeRef.current = theme.mode;
80
+ lastWorkspaceRef.current = workspaceId;
81
+ }
82
+ const src = srcRef.current.src;
83
+ const postNow = (0, import_react.useCallback)(
84
+ (message) => {
85
+ iframeRef.current?.contentWindow?.postMessage(message, widgetOrigin);
86
+ },
87
+ [widgetOrigin]
88
+ );
89
+ const post = (0, import_react.useCallback)(
90
+ (message) => {
91
+ if (!ready || !iframeRef.current?.contentWindow) {
92
+ pendingRef.current = message;
93
+ return;
94
+ }
95
+ postNow(message);
96
+ },
97
+ [ready, postNow]
98
+ );
99
+ (0, import_react.useEffect)(() => {
100
+ setLoaded(false);
101
+ setReady(false);
102
+ pendingRef.current = null;
103
+ }, [mountKey]);
104
+ (0, import_react.useEffect)(() => {
105
+ function handle(event) {
106
+ if (widgetOrigin !== "*" && event.origin !== widgetOrigin) return;
107
+ if (event.source !== iframeRef.current?.contentWindow) return;
108
+ const data = event.data;
109
+ if (!data?.type) return;
110
+ if (data.type === "chat:ready") {
111
+ setReady(true);
112
+ navigation.onReady?.();
113
+ } else if (data.type === "chat:navigated") {
114
+ navigation.onNavigated(normalizeChatId(data.payload?.chatId));
115
+ } else if (isNotificationType(data.type)) {
116
+ const payload = data.payload;
117
+ if (data.type === "chat:processing-complete") {
118
+ notifications.onProcessingComplete(payload);
119
+ } else {
120
+ notifications.onAttentionNeeded(payload);
121
+ }
122
+ }
123
+ }
124
+ window.addEventListener("message", handle);
125
+ return () => window.removeEventListener("message", handle);
126
+ }, [navigation, notifications, widgetOrigin]);
127
+ (0, import_react.useEffect)(() => {
128
+ if (!ready) return;
129
+ const pending = pendingRef.current;
130
+ if (pending) {
131
+ pendingRef.current = null;
132
+ postNow(pending);
133
+ }
134
+ }, [ready, postNow]);
135
+ (0, import_react.useEffect)(() => {
136
+ if (!ready) return;
137
+ if (lastWorkspaceRef.current === workspaceId) return;
138
+ postNow({ type: "chat:switch-workspace", payload: { workspaceId } });
139
+ lastWorkspaceRef.current = workspaceId;
140
+ }, [ready, workspaceId, postNow]);
141
+ (0, import_react.useEffect)(() => {
142
+ if (!ready) return;
143
+ if (lastThemeRef.current === theme.mode) return;
144
+ postNow({ type: "chat:set-theme", payload: { theme: theme.mode } });
145
+ lastThemeRef.current = theme.mode;
146
+ }, [ready, theme.mode, postNow]);
147
+ (0, import_react.useImperativeHandle)(
148
+ ref,
149
+ () => ({
150
+ newChat: () => post({ type: "chat:new" }),
151
+ navigateTo: (chatId) => post({ type: "chat:navigate", payload: { chatId } }),
152
+ reload: (seedChatId) => {
153
+ seedRef.current = normalizeChatId(seedChatId);
154
+ setReloadNonce((n) => n + 1);
155
+ }
156
+ }),
157
+ [post]
158
+ );
159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
160
+ "div",
161
+ {
162
+ className,
163
+ style: { position: "relative", height: "100%", width: "100%", ...style },
164
+ children: [
165
+ !loaded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
166
+ "div",
167
+ {
168
+ style: {
169
+ position: "absolute",
170
+ inset: 0,
171
+ display: "flex",
172
+ alignItems: "center",
173
+ justifyContent: "center",
174
+ borderRadius,
175
+ backgroundColor: "var(--alias-surface-base)"
176
+ },
177
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
+ "span",
179
+ {
180
+ style: {
181
+ color: "var(--alias-text---icons-muted)",
182
+ fontFamily: "var(--font-family-font-family-body)",
183
+ fontSize: "var(--font-size-font-size-sm)"
184
+ },
185
+ children: loadingLabel
186
+ }
187
+ )
188
+ }
189
+ ) : null,
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: "100%", width: "100%", overflow: "hidden", borderRadius }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
191
+ "iframe",
192
+ {
193
+ ref: iframeRef,
194
+ title: "Streamoid Copilot",
195
+ src,
196
+ style: { height: "100%", width: "100%", border: 0, background: "transparent" },
197
+ onLoad: () => setLoaded(true),
198
+ referrerPolicy: "strict-origin-when-cross-origin",
199
+ allow: "clipboard-write"
200
+ },
201
+ mountKey
202
+ ) })
203
+ ]
204
+ }
205
+ );
206
+ }
207
+ );
package/dist/index.mjs ADDED
@@ -0,0 +1,192 @@
1
+ // src/AgentPanel.tsx
2
+ import {
3
+ forwardRef,
4
+ useCallback,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useMemo,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ function normalizeChatId(value) {
13
+ const trimmed = value?.trim();
14
+ return trimmed ? trimmed : null;
15
+ }
16
+ function buildSrc(adapters, seedChatId) {
17
+ const { auth, workspace, theme, config } = adapters;
18
+ const base = config.widgetUrl.endsWith("/widget/") ? config.widgetUrl : `${config.widgetUrl.replace(/\/+$/, "")}/widget/`;
19
+ const url = new URL(base);
20
+ if (workspace.current.id) url.searchParams.set("workspaceId", workspace.current.id);
21
+ if (auth.user.id) url.searchParams.set("userId", auth.user.id);
22
+ if (auth.user.email) url.searchParams.set("email", auth.user.email);
23
+ if (auth.user.specialization != null) {
24
+ url.searchParams.set("userSpecialization", String(auth.user.specialization));
25
+ }
26
+ url.searchParams.set("theme", theme.mode);
27
+ if (typeof window !== "undefined") {
28
+ url.searchParams.set("parentOrigin", window.location.origin);
29
+ }
30
+ if (config.hide?.sidepane) url.searchParams.set("hideSidepane", "1");
31
+ if (config.hide?.newBrief) url.searchParams.set("hideNewBrief", "1");
32
+ if (config.hide?.notifications) url.searchParams.set("hideNotifications", "1");
33
+ return `${url.toString()}#${seedChatId ? `/chat/${seedChatId}` : "/"}`;
34
+ }
35
+ function isNotificationType(type) {
36
+ return type === "chat:processing-complete" || type === "chat:attention-needed";
37
+ }
38
+ var AgentPanel = forwardRef(
39
+ function AgentPanel2({ adapters, className, style, borderRadius, loadingLabel = "Loading\u2026" }, ref) {
40
+ const { auth, workspace, theme, config, navigation, notifications } = adapters;
41
+ const workspaceId = workspace.current.id;
42
+ const iframeRef = useRef(null);
43
+ const widgetOrigin = useMemo(() => {
44
+ try {
45
+ return new URL(config.widgetUrl).origin;
46
+ } catch {
47
+ return "*";
48
+ }
49
+ }, [config.widgetUrl]);
50
+ const [loaded, setLoaded] = useState(false);
51
+ const [ready, setReady] = useState(false);
52
+ const [reloadNonce, setReloadNonce] = useState(0);
53
+ const seedRef = useRef(normalizeChatId(navigation.seedChatId));
54
+ const mountKey = `${auth.user.id}|${auth.user.specialization ?? ""}|${reloadNonce}`;
55
+ const lastThemeRef = useRef(null);
56
+ const lastWorkspaceRef = useRef(null);
57
+ const pendingRef = useRef(null);
58
+ const srcRef = useRef({ key: "", src: "" });
59
+ if (srcRef.current.key !== mountKey) {
60
+ srcRef.current = { key: mountKey, src: buildSrc(adapters, seedRef.current) };
61
+ lastThemeRef.current = theme.mode;
62
+ lastWorkspaceRef.current = workspaceId;
63
+ }
64
+ const src = srcRef.current.src;
65
+ const postNow = useCallback(
66
+ (message) => {
67
+ iframeRef.current?.contentWindow?.postMessage(message, widgetOrigin);
68
+ },
69
+ [widgetOrigin]
70
+ );
71
+ const post = useCallback(
72
+ (message) => {
73
+ if (!ready || !iframeRef.current?.contentWindow) {
74
+ pendingRef.current = message;
75
+ return;
76
+ }
77
+ postNow(message);
78
+ },
79
+ [ready, postNow]
80
+ );
81
+ useEffect(() => {
82
+ setLoaded(false);
83
+ setReady(false);
84
+ pendingRef.current = null;
85
+ }, [mountKey]);
86
+ useEffect(() => {
87
+ function handle(event) {
88
+ if (widgetOrigin !== "*" && event.origin !== widgetOrigin) return;
89
+ if (event.source !== iframeRef.current?.contentWindow) return;
90
+ const data = event.data;
91
+ if (!data?.type) return;
92
+ if (data.type === "chat:ready") {
93
+ setReady(true);
94
+ navigation.onReady?.();
95
+ } else if (data.type === "chat:navigated") {
96
+ navigation.onNavigated(normalizeChatId(data.payload?.chatId));
97
+ } else if (isNotificationType(data.type)) {
98
+ const payload = data.payload;
99
+ if (data.type === "chat:processing-complete") {
100
+ notifications.onProcessingComplete(payload);
101
+ } else {
102
+ notifications.onAttentionNeeded(payload);
103
+ }
104
+ }
105
+ }
106
+ window.addEventListener("message", handle);
107
+ return () => window.removeEventListener("message", handle);
108
+ }, [navigation, notifications, widgetOrigin]);
109
+ useEffect(() => {
110
+ if (!ready) return;
111
+ const pending = pendingRef.current;
112
+ if (pending) {
113
+ pendingRef.current = null;
114
+ postNow(pending);
115
+ }
116
+ }, [ready, postNow]);
117
+ useEffect(() => {
118
+ if (!ready) return;
119
+ if (lastWorkspaceRef.current === workspaceId) return;
120
+ postNow({ type: "chat:switch-workspace", payload: { workspaceId } });
121
+ lastWorkspaceRef.current = workspaceId;
122
+ }, [ready, workspaceId, postNow]);
123
+ useEffect(() => {
124
+ if (!ready) return;
125
+ if (lastThemeRef.current === theme.mode) return;
126
+ postNow({ type: "chat:set-theme", payload: { theme: theme.mode } });
127
+ lastThemeRef.current = theme.mode;
128
+ }, [ready, theme.mode, postNow]);
129
+ useImperativeHandle(
130
+ ref,
131
+ () => ({
132
+ newChat: () => post({ type: "chat:new" }),
133
+ navigateTo: (chatId) => post({ type: "chat:navigate", payload: { chatId } }),
134
+ reload: (seedChatId) => {
135
+ seedRef.current = normalizeChatId(seedChatId);
136
+ setReloadNonce((n) => n + 1);
137
+ }
138
+ }),
139
+ [post]
140
+ );
141
+ return /* @__PURE__ */ jsxs(
142
+ "div",
143
+ {
144
+ className,
145
+ style: { position: "relative", height: "100%", width: "100%", ...style },
146
+ children: [
147
+ !loaded ? /* @__PURE__ */ jsx(
148
+ "div",
149
+ {
150
+ style: {
151
+ position: "absolute",
152
+ inset: 0,
153
+ display: "flex",
154
+ alignItems: "center",
155
+ justifyContent: "center",
156
+ borderRadius,
157
+ backgroundColor: "var(--alias-surface-base)"
158
+ },
159
+ children: /* @__PURE__ */ jsx(
160
+ "span",
161
+ {
162
+ style: {
163
+ color: "var(--alias-text---icons-muted)",
164
+ fontFamily: "var(--font-family-font-family-body)",
165
+ fontSize: "var(--font-size-font-size-sm)"
166
+ },
167
+ children: loadingLabel
168
+ }
169
+ )
170
+ }
171
+ ) : null,
172
+ /* @__PURE__ */ jsx("div", { style: { height: "100%", width: "100%", overflow: "hidden", borderRadius }, children: /* @__PURE__ */ jsx(
173
+ "iframe",
174
+ {
175
+ ref: iframeRef,
176
+ title: "Streamoid Copilot",
177
+ src,
178
+ style: { height: "100%", width: "100%", border: 0, background: "transparent" },
179
+ onLoad: () => setLoaded(true),
180
+ referrerPolicy: "strict-origin-when-cross-origin",
181
+ allow: "clipboard-write"
182
+ },
183
+ mountKey
184
+ ) })
185
+ ]
186
+ }
187
+ );
188
+ }
189
+ );
190
+ export {
191
+ AgentPanel
192
+ };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@streamoid/agent",
3
+ "version": "0.1.0",
4
+ "main": "./dist/index.js",
5
+ "module": "./dist/index.mjs",
6
+ "types": "./dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "sideEffects": false,
14
+ "scripts": {
15
+ "build": "npm run build:js && npm run build:dts",
16
+ "build:js": "esbuild src/index.ts --bundle --external:react --external:react-dom --outfile=dist/index.js --format=cjs && esbuild src/index.ts --bundle --external:react --external:react-dom --outfile=dist/index.mjs --format=esm",
17
+ "build:dts": "tsup src/index.ts --dts-only",
18
+ "lint": "eslint src/",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "peerDependencies": {
22
+ "react": "^18.0.0 || ^19.0.0",
23
+ "react-dom": "^18.0.0 || ^19.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "^19.0.0",
27
+ "@types/react-dom": "^19.0.0",
28
+ "react": "^19.0.0",
29
+ "react-dom": "^19.0.0",
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.0.0"
32
+ }
33
+ }