skybridge 0.0.0-dev.66b8f6b → 0.0.0-dev.6b57ab9
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 +321 -1
- package/dist/src/server/index.d.ts +4 -0
- package/dist/{server/index.d.ts → src/server/index.js} +1 -1
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/inferUtilityTypes.d.ts +46 -0
- package/dist/src/server/inferUtilityTypes.js +2 -0
- package/dist/src/server/inferUtilityTypes.js.map +1 -0
- package/dist/src/server/server.d.ts +37 -0
- package/dist/src/server/server.js +62 -0
- package/dist/src/server/server.js.map +1 -0
- package/dist/src/server/templateHelper.d.ts +15 -0
- package/dist/src/server/templateHelper.js +29 -0
- package/dist/src/server/templateHelper.js.map +1 -0
- package/dist/src/server/templates/development.hbs +12 -0
- package/dist/src/server/templates/production.hbs +6 -0
- package/dist/{server → src/server}/widgetsDevServer.d.ts +5 -2
- package/dist/{server → src/server}/widgetsDevServer.js +5 -2
- package/dist/src/server/widgetsDevServer.js.map +1 -0
- package/dist/src/test/utils.d.ts +69 -0
- package/dist/src/test/utils.js +136 -0
- package/dist/src/test/utils.js.map +1 -0
- package/dist/src/test/widget.test.d.ts +1 -0
- package/dist/src/test/widget.test.js +90 -0
- package/dist/src/test/widget.test.js.map +1 -0
- package/dist/src/web/hooks/index.d.ts +14 -0
- package/dist/src/web/hooks/index.js +15 -0
- package/dist/src/web/hooks/index.js.map +1 -0
- package/dist/src/web/hooks/use-call-tool.d.ts +62 -0
- package/dist/src/web/hooks/use-call-tool.js +68 -0
- package/dist/src/web/hooks/use-call-tool.js.map +1 -0
- package/dist/src/web/hooks/use-call-tool.test.d.ts +1 -0
- package/dist/src/web/hooks/use-call-tool.test.js +163 -0
- package/dist/src/web/hooks/use-call-tool.test.js.map +1 -0
- package/dist/src/web/hooks/use-display-mode.d.ts +4 -0
- package/dist/src/web/hooks/use-display-mode.js +7 -0
- package/dist/src/web/hooks/use-display-mode.js.map +1 -0
- package/dist/src/web/hooks/use-display-mode.test.d.ts +1 -0
- package/dist/src/web/hooks/use-display-mode.test.js +40 -0
- package/dist/src/web/hooks/use-display-mode.test.js.map +1 -0
- package/dist/src/web/hooks/use-files.d.ts +10 -0
- package/dist/src/web/hooks/use-files.js +7 -0
- package/dist/src/web/hooks/use-files.js.map +1 -0
- package/dist/src/web/hooks/use-files.test.d.ts +1 -0
- package/dist/src/web/hooks/use-files.test.js +29 -0
- package/dist/src/web/hooks/use-files.test.js.map +1 -0
- package/dist/src/web/hooks/use-locale.d.ts +1 -0
- package/dist/src/web/hooks/use-locale.js +5 -0
- package/dist/src/web/hooks/use-locale.js.map +1 -0
- package/dist/src/web/hooks/use-locale.test.d.ts +1 -0
- package/dist/src/web/hooks/use-locale.test.js +21 -0
- package/dist/src/web/hooks/use-locale.test.js.map +1 -0
- package/dist/src/web/hooks/use-open-external.d.ts +1 -0
- package/dist/src/web/hooks/use-open-external.js +6 -0
- package/dist/src/web/hooks/use-open-external.js.map +1 -0
- package/dist/src/web/hooks/use-open-external.test.d.ts +1 -0
- package/dist/src/web/hooks/use-open-external.test.js +24 -0
- package/dist/src/web/hooks/use-open-external.test.js.map +1 -0
- package/dist/{web → src/web/hooks}/use-openai-global.d.ts +1 -1
- package/dist/{web → src/web/hooks}/use-openai-global.js +4 -2
- package/dist/src/web/hooks/use-openai-global.js.map +1 -0
- package/dist/src/web/hooks/use-request-modal.d.ts +6 -0
- package/dist/src/web/hooks/use-request-modal.js +9 -0
- package/dist/src/web/hooks/use-request-modal.js.map +1 -0
- package/dist/src/web/hooks/use-request-modal.test.d.ts +1 -0
- package/dist/src/web/hooks/use-request-modal.test.js +24 -0
- package/dist/src/web/hooks/use-request-modal.test.js.map +1 -0
- package/dist/src/web/hooks/use-send-follow-up-message.d.ts +1 -0
- package/dist/src/web/hooks/use-send-follow-up-message.js +11 -0
- package/dist/src/web/hooks/use-send-follow-up-message.js.map +1 -0
- package/dist/src/web/hooks/use-theme.d.ts +1 -0
- package/dist/src/web/hooks/use-theme.js +5 -0
- package/dist/src/web/hooks/use-theme.js.map +1 -0
- package/dist/src/web/hooks/use-theme.test.d.ts +1 -0
- package/dist/src/web/hooks/use-theme.test.js +26 -0
- package/dist/src/web/hooks/use-theme.test.js.map +1 -0
- package/dist/src/web/hooks/use-tool-info.d.ts +25 -0
- package/dist/src/web/hooks/use-tool-info.js +22 -0
- package/dist/src/web/hooks/use-tool-info.js.map +1 -0
- package/dist/src/web/hooks/use-tool-info.test-d.d.ts +1 -0
- package/dist/src/web/hooks/use-tool-info.test-d.js +74 -0
- package/dist/src/web/hooks/use-tool-info.test-d.js.map +1 -0
- package/dist/src/web/hooks/use-tool-info.test.d.ts +1 -0
- package/dist/src/web/hooks/use-tool-info.test.js +59 -0
- package/dist/src/web/hooks/use-tool-info.test.js.map +1 -0
- package/dist/src/web/hooks/use-tool-output.d.ts +4 -0
- package/dist/src/web/hooks/use-tool-output.js +9 -0
- package/dist/src/web/hooks/use-tool-output.js.map +1 -0
- package/dist/src/web/hooks/use-tool-response-metadata.d.ts +4 -0
- package/dist/src/web/hooks/use-tool-response-metadata.js +8 -0
- package/dist/src/web/hooks/use-tool-response-metadata.js.map +1 -0
- package/dist/src/web/hooks/use-user-agent.d.ts +1 -0
- package/dist/src/web/hooks/use-user-agent.js +5 -0
- package/dist/src/web/hooks/use-user-agent.js.map +1 -0
- package/dist/src/web/hooks/use-user-agent.test.d.ts +1 -0
- package/dist/src/web/hooks/use-user-agent.test.js +31 -0
- package/dist/src/web/hooks/use-user-agent.test.js.map +1 -0
- package/dist/src/web/hooks/use-widget-state.d.ts +4 -0
- package/dist/src/web/hooks/use-widget-state.js +30 -0
- package/dist/src/web/hooks/use-widget-state.js.map +1 -0
- package/dist/src/web/hooks/use-widget-state.test.d.ts +1 -0
- package/dist/src/web/hooks/use-widget-state.test.js +60 -0
- package/dist/src/web/hooks/use-widget-state.test.js.map +1 -0
- package/dist/src/web/index.d.ts +5 -0
- package/dist/src/web/index.js +6 -0
- package/dist/src/web/index.js.map +1 -0
- package/dist/src/web/mount-widget.js +19 -0
- package/dist/src/web/mount-widget.js.map +1 -0
- package/dist/src/web/plugin.d.ts +2 -0
- package/dist/src/web/plugin.js +28 -0
- package/dist/src/web/plugin.js.map +1 -0
- package/dist/src/web/proxy.d.ts +1 -0
- package/dist/src/web/proxy.js +48 -0
- package/dist/src/web/proxy.js.map +1 -0
- package/dist/src/web/typed-hooks.d.ts +107 -0
- package/dist/src/web/typed-hooks.js +111 -0
- package/dist/src/web/typed-hooks.js.map +1 -0
- package/dist/src/web/typed-hooks.test-d.d.ts +1 -0
- package/dist/src/web/typed-hooks.test-d.js +87 -0
- package/dist/src/web/typed-hooks.test-d.js.map +1 -0
- package/dist/src/web/typed-hooks.test.d.ts +1 -0
- package/dist/src/web/typed-hooks.test.js +17 -0
- package/dist/src/web/typed-hooks.test.js.map +1 -0
- package/dist/{web → src/web}/types.d.ts +39 -15
- package/dist/src/web/types.js.map +1 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +8 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +26 -12
- package/dist/server/index.js +0 -4
- package/dist/server/index.js.map +0 -1
- package/dist/server/middleware.d.ts +0 -3
- package/dist/server/middleware.js +0 -47
- package/dist/server/middleware.js.map +0 -1
- package/dist/server/server.d.ts +0 -12
- package/dist/server/server.js +0 -70
- package/dist/server/server.js.map +0 -1
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/web/index.d.ts +0 -4
- package/dist/web/index.js +0 -5
- package/dist/web/index.js.map +0 -1
- package/dist/web/mount-widget.js +0 -10
- package/dist/web/mount-widget.js.map +0 -1
- package/dist/web/types.js.map +0 -1
- package/dist/web/use-openai-global.js.map +0 -1
- package/dist/web/use-tool-output.d.ts +0 -3
- package/dist/web/use-tool-output.js +0 -5
- package/dist/web/use-tool-output.js.map +0 -1
- /package/dist/{web → src/web}/mount-widget.d.ts +0 -0
- /package/dist/{web → src/web}/types.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-tool-output.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-tool-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAEjC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useOpenAiGlobal } from "./use-openai-global.js";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated This hook is deprecated. Use `useToolInfo()` instead and access the `responseMetadata` property.
|
|
4
|
+
*/
|
|
5
|
+
export function useToolResponseMetadata() {
|
|
6
|
+
return useOpenAiGlobal("toolResponseMetadata");
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=use-tool-response-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-tool-response-metadata.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-tool-response-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,eAAe,CAAC,sBAAsB,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useUserAgent(): import("../types.js").UserAgent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-user-agent.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-user-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,UAAU,YAAY;IAC1B,OAAO,eAAe,CAAC,WAAW,CAAE,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useUserAgent } from "./use-user-agent.js";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach, } from "vitest";
|
|
3
|
+
import { renderHook } from "@testing-library/react";
|
|
4
|
+
describe("useUserAgent", () => {
|
|
5
|
+
let OpenaiMock;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
OpenaiMock = {
|
|
8
|
+
userAgent: {
|
|
9
|
+
device: { type: "mobile" },
|
|
10
|
+
capabilities: { hover: false, touch: true },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
vi.stubGlobal("openai", OpenaiMock);
|
|
14
|
+
});
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
vi.unstubAllGlobals();
|
|
17
|
+
vi.resetAllMocks();
|
|
18
|
+
});
|
|
19
|
+
it("should return the current user agent from window.openai.userAgent", () => {
|
|
20
|
+
OpenaiMock.userAgent = {
|
|
21
|
+
device: { type: "mobile" },
|
|
22
|
+
capabilities: { hover: false, touch: true },
|
|
23
|
+
};
|
|
24
|
+
const { result } = renderHook(() => useUserAgent());
|
|
25
|
+
expect(result.current).toEqual({
|
|
26
|
+
device: { type: "mobile" },
|
|
27
|
+
capabilities: { hover: false, touch: true },
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=use-user-agent.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-user-agent.test.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-user-agent.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EACL,QAAQ,EACR,EAAE,EACF,MAAM,EACN,EAAE,EACF,UAAU,EACV,SAAS,GAEV,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGpD,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,IAAI,UAEH,CAAC;IAEF,UAAU,CAAC,GAAG,EAAE;QACd,UAAU,GAAG;YACX,SAAS,EAAE;gBACT,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;aAC5C;SACF,CAAC;QACF,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,gBAAgB,EAAE,CAAC;QACtB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,UAAU,CAAC,SAAS,GAAG;YACrB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;SAC5C,CAAC;QACF,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC;QAEpD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;YAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,YAAY,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE;SAC5C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type SetStateAction } from "react";
|
|
2
|
+
import type { UnknownObject } from "../types.js";
|
|
3
|
+
export declare function useWidgetState<T extends UnknownObject>(defaultState: T | (() => T)): readonly [T, (state: SetStateAction<T>) => void];
|
|
4
|
+
export declare function useWidgetState<T extends UnknownObject>(defaultState?: T | (() => T | null) | null): readonly [T | null, (state: SetStateAction<T | null>) => void];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { useOpenAiGlobal } from "./use-openai-global.js";
|
|
3
|
+
export function useWidgetState(defaultState) {
|
|
4
|
+
const widgetStateFromWindow = useOpenAiGlobal("widgetState");
|
|
5
|
+
const [widgetState, _setWidgetState] = useState(() => {
|
|
6
|
+
if (widgetStateFromWindow !== null) {
|
|
7
|
+
return widgetStateFromWindow;
|
|
8
|
+
}
|
|
9
|
+
return typeof defaultState === "function"
|
|
10
|
+
? defaultState()
|
|
11
|
+
: defaultState ?? null;
|
|
12
|
+
});
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Fixes openai implementation bug
|
|
15
|
+
if (widgetStateFromWindow !== null) {
|
|
16
|
+
_setWidgetState(widgetStateFromWindow);
|
|
17
|
+
}
|
|
18
|
+
}, [widgetStateFromWindow]);
|
|
19
|
+
const setWidgetState = useCallback((state) => {
|
|
20
|
+
_setWidgetState((prevState) => {
|
|
21
|
+
const newState = typeof state === "function" ? state(prevState) : state;
|
|
22
|
+
if (newState !== null) {
|
|
23
|
+
window.openai.setWidgetState(newState);
|
|
24
|
+
}
|
|
25
|
+
return newState;
|
|
26
|
+
});
|
|
27
|
+
}, [window.openai.setWidgetState]);
|
|
28
|
+
return [widgetState, setWidgetState];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=use-widget-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-widget-state.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-widget-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAuB,MAAM,OAAO,CAAC;AAE9E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAQzD,MAAM,UAAU,cAAc,CAC5B,YAA0C;IAE1C,MAAM,qBAAqB,GAAG,eAAe,CAAC,aAAa,CAAM,CAAC;IAElE,MAAM,CAAC,WAAW,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE;QAC7D,IAAI,qBAAqB,KAAK,IAAI,EAAE,CAAC;YACnC,OAAO,qBAAqB,CAAC;QAC/B,CAAC;QAED,OAAO,OAAO,YAAY,KAAK,UAAU;YACvC,CAAC,CAAC,YAAY,EAAE;YAChB,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,kCAAkC;QAClC,IAAI,qBAAqB,KAAK,IAAI,EAAE,CAAC;YACnC,eAAe,CAAC,qBAAqB,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAE5B,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,KAA+B,EAAE,EAAE;QAClC,eAAe,CAAC,CAAC,SAAS,EAAE,EAAE;YAC5B,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAExE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACzC,CAAC;YAED,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAC/B,CAAC;IAEF,OAAO,CAAC,WAAW,EAAE,cAAc,CAAU,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useWidgetState } from "./use-widget-state.js";
|
|
2
|
+
import { describe, it, expect, vi, beforeEach, afterEach, } from "vitest";
|
|
3
|
+
import { renderHook, act } from "@testing-library/react";
|
|
4
|
+
describe("useWidgetState", () => {
|
|
5
|
+
let OpenaiMock;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
OpenaiMock = {
|
|
8
|
+
widgetState: null,
|
|
9
|
+
setWidgetState: vi.fn().mockResolvedValue(undefined),
|
|
10
|
+
};
|
|
11
|
+
vi.stubGlobal("openai", OpenaiMock);
|
|
12
|
+
});
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.unstubAllGlobals();
|
|
15
|
+
vi.resetAllMocks();
|
|
16
|
+
});
|
|
17
|
+
const defaultState = { count: 0, name: "test" };
|
|
18
|
+
const windowState = { count: 5, name: "window" };
|
|
19
|
+
it("should initialize with default state when window.openai.widgetState is null", () => {
|
|
20
|
+
OpenaiMock.widgetState = null;
|
|
21
|
+
const { result } = renderHook(() => useWidgetState(defaultState));
|
|
22
|
+
expect(result.current[0]).toEqual(defaultState);
|
|
23
|
+
});
|
|
24
|
+
it("should initialize with window.openai.widgetState when available", () => {
|
|
25
|
+
OpenaiMock.widgetState = windowState;
|
|
26
|
+
const { result } = renderHook(() => useWidgetState(defaultState));
|
|
27
|
+
expect(result.current[0]).toEqual(windowState);
|
|
28
|
+
});
|
|
29
|
+
it("should call window.openai.setWidgetState when setWidgetState is called with a new state", async () => {
|
|
30
|
+
const { result } = renderHook(() => useWidgetState(defaultState));
|
|
31
|
+
const newState = { count: 10, name: "updated" };
|
|
32
|
+
act(() => {
|
|
33
|
+
result.current[1](newState);
|
|
34
|
+
});
|
|
35
|
+
expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith(newState);
|
|
36
|
+
expect(result.current[0]).toEqual(newState);
|
|
37
|
+
});
|
|
38
|
+
it("should call window.openai.setWidgetState when setWidgetState is called with a function updater", async () => {
|
|
39
|
+
const { result } = renderHook(() => useWidgetState(defaultState));
|
|
40
|
+
act(() => {
|
|
41
|
+
result.current[1]((prev) => ({ ...prev, count: prev.count + 1 }));
|
|
42
|
+
});
|
|
43
|
+
expect(OpenaiMock.setWidgetState).toHaveBeenCalledWith({
|
|
44
|
+
count: 1,
|
|
45
|
+
name: "test",
|
|
46
|
+
});
|
|
47
|
+
expect(result.current[0]).toEqual({ count: 1, name: "test" });
|
|
48
|
+
});
|
|
49
|
+
it("should update state when window.openai.widgetState changes", () => {
|
|
50
|
+
OpenaiMock.widgetState = defaultState;
|
|
51
|
+
const { result, rerender } = renderHook(() => useWidgetState(defaultState));
|
|
52
|
+
expect(result.current[0]).toEqual(defaultState);
|
|
53
|
+
// Simulate window.openai.widgetState changing
|
|
54
|
+
OpenaiMock.widgetState = windowState;
|
|
55
|
+
// Trigger re-render to simulate the useEffect running
|
|
56
|
+
rerender();
|
|
57
|
+
expect(result.current[0]).toEqual(windowState);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=use-widget-state.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-widget-state.test.js","sourceRoot":"","sources":["../../../../src/web/hooks/use-widget-state.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/web/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
import { createElement, StrictMode } from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { installOpenAILoggingProxy } from "./proxy.js";
|
|
5
|
+
let rootInstance = null;
|
|
6
|
+
export const mountWidget = (component) => {
|
|
7
|
+
const rootElement = document.getElementById("root");
|
|
8
|
+
if (!rootElement) {
|
|
9
|
+
throw new Error("Root element not found");
|
|
10
|
+
}
|
|
11
|
+
if (!rootInstance) {
|
|
12
|
+
rootInstance = createRoot(rootElement);
|
|
13
|
+
}
|
|
14
|
+
if (import.meta.env.DEV) {
|
|
15
|
+
installOpenAILoggingProxy();
|
|
16
|
+
}
|
|
17
|
+
rootInstance.render(createElement(StrictMode, null, component));
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=mount-widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-widget.js","sourceRoot":"","sources":["../../../src/web/mount-widget.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,UAAU,EAAa,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAEvD,IAAI,YAAY,GAAgB,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,SAA0B,EAAE,EAAE;IACxD,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACxB,yBAAyB,EAAE,CAAC;IAC9B,CAAC;IAED,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AAClE,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function skybridge() {
|
|
2
|
+
return {
|
|
3
|
+
name: "skybridge",
|
|
4
|
+
async config(config) {
|
|
5
|
+
// Dynamic imports to ensure Node modules are only loaded in Node.js context
|
|
6
|
+
const { globSync } = await import("node:fs");
|
|
7
|
+
const { resolve } = await import("node:path");
|
|
8
|
+
const projectRoot = config.root || process.cwd();
|
|
9
|
+
const widgetsPattern = resolve(projectRoot, "src/widgets/*.{js,ts,jsx,tsx,html}");
|
|
10
|
+
const input = Object.fromEntries(globSync(widgetsPattern).map((file) => [
|
|
11
|
+
file.match(/src\/widgets\/(.+)\.tsx$/)?.[1],
|
|
12
|
+
file,
|
|
13
|
+
]));
|
|
14
|
+
return {
|
|
15
|
+
base: "/assets",
|
|
16
|
+
build: {
|
|
17
|
+
manifest: true,
|
|
18
|
+
minify: true,
|
|
19
|
+
cssCodeSplit: false,
|
|
20
|
+
rollupOptions: {
|
|
21
|
+
input,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS;IACvB,OAAO;QACL,IAAI,EAAE,WAAW;QAEjB,KAAK,CAAC,MAAM,CAAC,MAAM;YACjB,4EAA4E;YAC5E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAC7C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YAE9C,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YACjD,MAAM,cAAc,GAAG,OAAO,CAC5B,WAAW,EACX,oCAAoC,CACrC,CAAC;YAEF,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAC9B,QAAQ,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACrC,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC3C,IAAI;aACL,CAAC,CACH,CAAC;YAEF,OAAO;gBACL,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE;oBACL,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;oBACZ,YAAY,EAAE,KAAK;oBACnB,aAAa,EAAE;wBACb,KAAK;qBACN;iBACF;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installOpenAILoggingProxy(): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const colors = {
|
|
2
|
+
brand: "#6366f1",
|
|
3
|
+
info: "#22223b",
|
|
4
|
+
success: "#22c55e",
|
|
5
|
+
error: "#ef4444",
|
|
6
|
+
};
|
|
7
|
+
export function installOpenAILoggingProxy() {
|
|
8
|
+
if (typeof window === "undefined" || !window.openai) {
|
|
9
|
+
console.warn("[openai-proxy] window.openai not found, skipping proxy installation");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const originalOpenAI = window.openai;
|
|
13
|
+
const handler = {
|
|
14
|
+
get(target, prop, receiver) {
|
|
15
|
+
const value = Reflect.get(target, prop, receiver);
|
|
16
|
+
if (typeof value !== "function") {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
return function (...args) {
|
|
20
|
+
const methodName = String(prop);
|
|
21
|
+
console.group(`%c[openai] %cmethod %c${methodName}`, `color: ${colors.brand}; font-weight: normal`, `color: ${colors.info}; font-weight: normal`, `color: ${colors.success}`);
|
|
22
|
+
console.log("%c← args:", `color: ${colors.info}`, args);
|
|
23
|
+
const result = value.apply(target, args);
|
|
24
|
+
if (result && typeof result.then === "function") {
|
|
25
|
+
return result.then((resolved) => {
|
|
26
|
+
console.log("%c→ resolved:", `color: ${colors.success}`, resolved);
|
|
27
|
+
console.groupEnd();
|
|
28
|
+
return resolved;
|
|
29
|
+
}, (error) => {
|
|
30
|
+
console.error("%c→ rejected:", `color: ${colors.error}`, error);
|
|
31
|
+
console.groupEnd();
|
|
32
|
+
throw error;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
console.log("%c→ returned:", `color: ${colors.success}`, result);
|
|
36
|
+
console.groupEnd();
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
set(target, prop, value, receiver) {
|
|
41
|
+
console.log(`%c[openai] %cupdate %c${String(prop)}`, `color: ${colors.brand}`, `color: ${colors.info}`, `color: ${colors.success}; font-weight: bold`, "←", value);
|
|
42
|
+
return Reflect.set(target, prop, value, receiver);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
window.openai = new Proxy(originalOpenAI, handler);
|
|
46
|
+
console.log("%c[openai-proxy] %cInstalled logging proxy for window.openai", `color: ${colors.brand}`, `color: ${colors.info}`);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../../src/web/proxy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { useCallTool } from "./hooks/use-call-tool.js";
|
|
2
|
+
import type { ToolPendingState, ToolSuccessState } from "./hooks/use-tool-info.js";
|
|
3
|
+
import type { McpServer, InferTools, AnyToolRegistry, ToolInput, ToolOutput } from "../server/index.js";
|
|
4
|
+
import type { CallToolArgs, UnknownObject } from "./types.js";
|
|
5
|
+
type TypedCallToolReturn<TInput, TOutput> = ReturnType<typeof useCallTool<TInput & CallToolArgs, {
|
|
6
|
+
structuredContent: TOutput & UnknownObject;
|
|
7
|
+
}>>;
|
|
8
|
+
type TypedToolInfoReturn<TInput extends UnknownObject, TOutput extends UnknownObject, TResponseMetadata extends UnknownObject> = ToolPendingState<TInput> | ToolSuccessState<TInput, TOutput, TResponseMetadata>;
|
|
9
|
+
/**
|
|
10
|
+
* Creates typed versions of skybridge hooks with full type inference
|
|
11
|
+
* for tool names, inputs, and outputs.
|
|
12
|
+
*
|
|
13
|
+
* This is the recommended way to use skybridge hooks in your widgets.
|
|
14
|
+
* Set this up once in a dedicated file and export the typed hooks for use across your app.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam T - The type of your McpServer instance. Use `typeof server`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // server/src/index.ts
|
|
21
|
+
* const server = new McpServer({ name: "my-app", version: "1.0" }, {})
|
|
22
|
+
* .widget("search-voyage", {}, {
|
|
23
|
+
* inputSchema: { destination: z.string() },
|
|
24
|
+
* outputSchema: { results: z.array(z.string()) },
|
|
25
|
+
* }, async ({ destination }) => {
|
|
26
|
+
* return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* export type AppType = typeof server;
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // web/src/skybridge.ts (one-time setup)
|
|
35
|
+
* import type { AppType } from "../server";
|
|
36
|
+
* import { createTypedHooks } from "skybridge/web";
|
|
37
|
+
*
|
|
38
|
+
* export const { useCallTool, useToolInfo } = createTypedHooks<AppType>();
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // web/src/widgets/search.tsx (usage)
|
|
44
|
+
* import { useCallTool, useToolInfo } from "../skybridge";
|
|
45
|
+
*
|
|
46
|
+
* export function SearchWidget() {
|
|
47
|
+
* const { callTool, data } = useCallTool("search-voyage");
|
|
48
|
+
* // ^ autocomplete for tool names
|
|
49
|
+
* callTool({ destination: "Spain" });
|
|
50
|
+
* // ^ autocomplete for input fields
|
|
51
|
+
*
|
|
52
|
+
* const toolInfo = useToolInfo<"search-voyage">();
|
|
53
|
+
* // ^ autocomplete for widget names
|
|
54
|
+
* // toolInfo.input is typed based on widget input schema
|
|
55
|
+
* // toolInfo.output is typed based on widget output schema
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare function createTypedHooks<T extends McpServer<AnyToolRegistry>>(): {
|
|
60
|
+
/**
|
|
61
|
+
* Typed version of `useCallTool` that provides autocomplete for tool names
|
|
62
|
+
* and type inference for inputs and outputs.
|
|
63
|
+
*
|
|
64
|
+
* @param name - The name of the widget to call. Autocompletes based on your server's widget registry.
|
|
65
|
+
* @returns A hook with typed `callTool` function and `data` property.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const { callTool, data, isPending } = useCallTool("search-voyage");
|
|
70
|
+
* // TypeScript knows callTool expects { destination: string }
|
|
71
|
+
* callTool({ destination: "Spain" });
|
|
72
|
+
*
|
|
73
|
+
* // data.structuredContent is typed based on your outputSchema
|
|
74
|
+
* if (data) {
|
|
75
|
+
* console.log(data.structuredContent.results);
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
useCallTool: <K extends keyof InferTools<T> & string>(name: K) => TypedCallToolReturn<InferTools<T>[K]["input"], InferTools<T>[K]["output"]>;
|
|
80
|
+
/**
|
|
81
|
+
* Typed version of `useToolInfo` that provides autocomplete for widget names
|
|
82
|
+
* and type inference for inputs, outputs, and responseMetadata.
|
|
83
|
+
*
|
|
84
|
+
* @typeParam K - The name of the widget. Autocompletes based on your server's widget registry.
|
|
85
|
+
* @returns A discriminated union with `status: "pending" | "success"` that narrows correctly.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const toolInfo = useToolInfo<"search-voyage">();
|
|
90
|
+
* // toolInfo.input is typed as { destination: string; ... }
|
|
91
|
+
* // toolInfo.output is typed as { results: Array<...>; ... } | undefined
|
|
92
|
+
* // toolInfo.status narrows correctly: "pending" | "success"
|
|
93
|
+
*
|
|
94
|
+
* if (toolInfo.isPending) {
|
|
95
|
+
* // TypeScript knows output is undefined here
|
|
96
|
+
* console.log(toolInfo.input.destination);
|
|
97
|
+
* }
|
|
98
|
+
*
|
|
99
|
+
* if (toolInfo.isSuccess) {
|
|
100
|
+
* // TypeScript knows output is defined here
|
|
101
|
+
* console.log(toolInfo.output.results);
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
useToolInfo: <K extends keyof InferTools<T> & string>() => TypedToolInfoReturn<ToolInput<T, K> & UnknownObject, ToolOutput<T, K> & UnknownObject, UnknownObject>;
|
|
106
|
+
};
|
|
107
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useCallTool } from "./hooks/use-call-tool.js";
|
|
2
|
+
import { useToolInfo } from "./hooks/use-tool-info.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates typed versions of skybridge hooks with full type inference
|
|
5
|
+
* for tool names, inputs, and outputs.
|
|
6
|
+
*
|
|
7
|
+
* This is the recommended way to use skybridge hooks in your widgets.
|
|
8
|
+
* Set this up once in a dedicated file and export the typed hooks for use across your app.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam T - The type of your McpServer instance. Use `typeof server`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // server/src/index.ts
|
|
15
|
+
* const server = new McpServer({ name: "my-app", version: "1.0" }, {})
|
|
16
|
+
* .widget("search-voyage", {}, {
|
|
17
|
+
* inputSchema: { destination: z.string() },
|
|
18
|
+
* outputSchema: { results: z.array(z.string()) },
|
|
19
|
+
* }, async ({ destination }) => {
|
|
20
|
+
* return { content: [{ type: "text", text: `Found trips to ${destination}` }] };
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* export type AppType = typeof server;
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // web/src/skybridge.ts (one-time setup)
|
|
29
|
+
* import type { AppType } from "../server";
|
|
30
|
+
* import { createTypedHooks } from "skybridge/web";
|
|
31
|
+
*
|
|
32
|
+
* export const { useCallTool, useToolInfo } = createTypedHooks<AppType>();
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* // web/src/widgets/search.tsx (usage)
|
|
38
|
+
* import { useCallTool, useToolInfo } from "../skybridge";
|
|
39
|
+
*
|
|
40
|
+
* export function SearchWidget() {
|
|
41
|
+
* const { callTool, data } = useCallTool("search-voyage");
|
|
42
|
+
* // ^ autocomplete for tool names
|
|
43
|
+
* callTool({ destination: "Spain" });
|
|
44
|
+
* // ^ autocomplete for input fields
|
|
45
|
+
*
|
|
46
|
+
* const toolInfo = useToolInfo<"search-voyage">();
|
|
47
|
+
* // ^ autocomplete for widget names
|
|
48
|
+
* // toolInfo.input is typed based on widget input schema
|
|
49
|
+
* // toolInfo.output is typed based on widget output schema
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function createTypedHooks() {
|
|
54
|
+
return {
|
|
55
|
+
/**
|
|
56
|
+
* Typed version of `useCallTool` that provides autocomplete for tool names
|
|
57
|
+
* and type inference for inputs and outputs.
|
|
58
|
+
*
|
|
59
|
+
* @param name - The name of the widget to call. Autocompletes based on your server's widget registry.
|
|
60
|
+
* @returns A hook with typed `callTool` function and `data` property.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const { callTool, data, isPending } = useCallTool("search-voyage");
|
|
65
|
+
* // TypeScript knows callTool expects { destination: string }
|
|
66
|
+
* callTool({ destination: "Spain" });
|
|
67
|
+
*
|
|
68
|
+
* // data.structuredContent is typed based on your outputSchema
|
|
69
|
+
* if (data) {
|
|
70
|
+
* console.log(data.structuredContent.results);
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
useCallTool: (name) => {
|
|
75
|
+
// Type assertion is safe here because the runtime types are compatible.
|
|
76
|
+
// The underlying hook accepts broader types, but we expose narrower, more specific types.
|
|
77
|
+
return useCallTool(name);
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Typed version of `useToolInfo` that provides autocomplete for widget names
|
|
81
|
+
* and type inference for inputs, outputs, and responseMetadata.
|
|
82
|
+
*
|
|
83
|
+
* @typeParam K - The name of the widget. Autocompletes based on your server's widget registry.
|
|
84
|
+
* @returns A discriminated union with `status: "pending" | "success"` that narrows correctly.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const toolInfo = useToolInfo<"search-voyage">();
|
|
89
|
+
* // toolInfo.input is typed as { destination: string; ... }
|
|
90
|
+
* // toolInfo.output is typed as { results: Array<...>; ... } | undefined
|
|
91
|
+
* // toolInfo.status narrows correctly: "pending" | "success"
|
|
92
|
+
*
|
|
93
|
+
* if (toolInfo.isPending) {
|
|
94
|
+
* // TypeScript knows output is undefined here
|
|
95
|
+
* console.log(toolInfo.input.destination);
|
|
96
|
+
* }
|
|
97
|
+
*
|
|
98
|
+
* if (toolInfo.isSuccess) {
|
|
99
|
+
* // TypeScript knows output is defined here
|
|
100
|
+
* console.log(toolInfo.output.results);
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
useToolInfo: () => {
|
|
105
|
+
// Type assertion is safe here because the runtime types are compatible.
|
|
106
|
+
// The underlying hook accepts broader types, but we expose narrower, more specific types.
|
|
107
|
+
return useToolInfo();
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=typed-hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typed-hooks.js","sourceRoot":"","sources":["../../../src/web/typed-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAgCvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,UAAU,gBAAgB;IAI9B,OAAO;QACL;;;;;;;;;;;;;;;;;;WAkBG;QACH,WAAW,EAAE,CACX,IAAO,EAIP,EAAE;YACF,wEAAwE;YACxE,0FAA0F;YAC1F,OAAO,WAAW,CAGhB,IAAI,CAGL,CAAC;QACJ,CAAC;QACD;;;;;;;;;;;;;;;;;;;;;;;;WAwBG;QACH,WAAW,EAAE,GAIX,EAAE;YACF,wEAAwE;YACxE,0FAA0F;YAC1F,OAAO,WAAW,EAQjB,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { expectTypeOf, test } from "vitest";
|
|
2
|
+
import { createTypedHooks } from "./typed-hooks.js";
|
|
3
|
+
import { createTestServer } from "../test/utils.js";
|
|
4
|
+
const server = createTestServer();
|
|
5
|
+
test("InferTools extracts the tool registry type (widgets + registerTool)", () => {
|
|
6
|
+
expectTypeOf().toHaveProperty("search-voyage");
|
|
7
|
+
expectTypeOf().toHaveProperty("get-trip-details");
|
|
8
|
+
expectTypeOf().toHaveProperty("no-input-widget");
|
|
9
|
+
expectTypeOf().toHaveProperty("calculate-price");
|
|
10
|
+
});
|
|
11
|
+
test("ToolNames returns a union of tool name literals (widgets + registerTool)", () => {
|
|
12
|
+
expectTypeOf().toEqualTypeOf();
|
|
13
|
+
});
|
|
14
|
+
test("ToolInput extracts the correct input type from Zod schema", () => {
|
|
15
|
+
expectTypeOf().toEqualTypeOf();
|
|
16
|
+
expectTypeOf().toEqualTypeOf();
|
|
17
|
+
expectTypeOf().toEqualTypeOf();
|
|
18
|
+
});
|
|
19
|
+
test("ToolOutput extracts the correct output type from Zod schema", () => {
|
|
20
|
+
expectTypeOf().toEqualTypeOf();
|
|
21
|
+
expectTypeOf().toEqualTypeOf();
|
|
22
|
+
expectTypeOf().toEqualTypeOf();
|
|
23
|
+
});
|
|
24
|
+
test("createTypedHooks provides autocomplete for tool names (widgets + registerTool)", () => {
|
|
25
|
+
const { useCallTool } = createTypedHooks();
|
|
26
|
+
useCallTool("search-voyage");
|
|
27
|
+
useCallTool("get-trip-details");
|
|
28
|
+
useCallTool("no-input-widget");
|
|
29
|
+
useCallTool("calculate-price");
|
|
30
|
+
// @ts-expect-error - "invalid-name" is not a valid tool name
|
|
31
|
+
useCallTool("invalid-name");
|
|
32
|
+
});
|
|
33
|
+
test("useCallTool returns correctly typed callTool function", () => {
|
|
34
|
+
const { useCallTool } = createTypedHooks();
|
|
35
|
+
const { callTool } = useCallTool("search-voyage");
|
|
36
|
+
callTool({ destination: "Spain" });
|
|
37
|
+
callTool({ destination: "France", departureDate: "2024-06-01" });
|
|
38
|
+
callTool({ destination: "Italy", maxPrice: 1000 });
|
|
39
|
+
const { callTool: calculateTool } = useCallTool("calculate-price");
|
|
40
|
+
calculateTool({ tripId: "123", passengers: 2 });
|
|
41
|
+
});
|
|
42
|
+
test("useCallTool returns correctly typed data", () => {
|
|
43
|
+
const { useCallTool } = createTypedHooks();
|
|
44
|
+
const { data } = useCallTool("search-voyage");
|
|
45
|
+
if (data) {
|
|
46
|
+
expectTypeOf(data.structuredContent).toExtend();
|
|
47
|
+
expectTypeOf(data.structuredContent.results).toBeArray();
|
|
48
|
+
expectTypeOf(data.structuredContent.totalCount).toBeNumber();
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
test("tools with no outputSchema have empty object output type", () => {
|
|
52
|
+
expectTypeOf().toEqualTypeOf();
|
|
53
|
+
});
|
|
54
|
+
test("createTypedHooks provides autocomplete for tool names in useToolInfo (widgets + registerTool)", () => {
|
|
55
|
+
const { useToolInfo } = createTypedHooks();
|
|
56
|
+
useToolInfo();
|
|
57
|
+
useToolInfo();
|
|
58
|
+
useToolInfo();
|
|
59
|
+
useToolInfo();
|
|
60
|
+
// @ts-expect-error - "invalid-name" is not a valid tool name
|
|
61
|
+
useToolInfo();
|
|
62
|
+
});
|
|
63
|
+
test("useToolInfo infers input types from ToolInput utility", () => {
|
|
64
|
+
const { useToolInfo } = createTypedHooks();
|
|
65
|
+
const toolInfo = useToolInfo();
|
|
66
|
+
expectTypeOf(toolInfo.input).toExtend();
|
|
67
|
+
const detailsInfo = useToolInfo();
|
|
68
|
+
expectTypeOf(detailsInfo.input).toExtend();
|
|
69
|
+
const calculateInfo = useToolInfo();
|
|
70
|
+
expectTypeOf(calculateInfo.input).toExtend();
|
|
71
|
+
});
|
|
72
|
+
test("useToolInfo infers output types from ToolOutput utility", () => {
|
|
73
|
+
const { useToolInfo } = createTypedHooks();
|
|
74
|
+
const toolInfo = useToolInfo();
|
|
75
|
+
if (toolInfo.status === "success") {
|
|
76
|
+
expectTypeOf(toolInfo.output).toExtend();
|
|
77
|
+
}
|
|
78
|
+
const detailsInfo = useToolInfo();
|
|
79
|
+
if (detailsInfo.status === "success") {
|
|
80
|
+
expectTypeOf(detailsInfo.output).toExtend();
|
|
81
|
+
}
|
|
82
|
+
const calculateInfo = useToolInfo();
|
|
83
|
+
if (calculateInfo.status === "success") {
|
|
84
|
+
expectTypeOf(calculateInfo.output).toExtend();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=typed-hooks.test-d.js.map
|