skybridge 0.0.0-dev.f8d2f8c → 0.0.0-dev.f8ef758
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 +123 -124
- package/dist/cli/build-helpers.d.ts +7 -0
- package/dist/cli/build-helpers.js +82 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.d.ts +1 -0
- package/dist/cli/build-helpers.test.js +64 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/detect-port.d.ts +2 -2
- package/dist/cli/detect-port.js +9 -20
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/use-typescript-check.js +1 -1
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -1
- package/dist/commands/build.js +18 -30
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/create.d.ts +9 -0
- package/dist/commands/create.js +30 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.js +16 -0
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +7 -1
- package/dist/commands/start.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +1 -0
- package/dist/server/asset-base-url-transform-plugin.js +16 -1
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +51 -1
- package/dist/server/asset-base-url-transform-plugin.test.js.map +1 -1
- package/dist/server/auth/discovery.d.ts +11 -0
- package/dist/server/auth/discovery.js +51 -0
- package/dist/server/auth/discovery.js.map +1 -0
- package/dist/server/auth/discovery.test.d.ts +1 -0
- package/dist/server/auth/discovery.test.js +93 -0
- package/dist/server/auth/discovery.test.js.map +1 -0
- package/dist/server/auth/index.d.ts +18 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +13 -0
- package/dist/server/auth/providers/auth0.js +18 -0
- package/dist/server/auth/providers/auth0.js.map +1 -0
- package/dist/server/auth/providers/auth0.test.d.ts +1 -0
- package/dist/server/auth/providers/auth0.test.js +33 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +14 -0
- package/dist/server/auth/providers/clerk.js +20 -0
- package/dist/server/auth/providers/clerk.js.map +1 -0
- package/dist/server/auth/providers/clerk.test.d.ts +1 -0
- package/dist/server/auth/providers/clerk.test.js +48 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +17 -0
- package/dist/server/auth/providers/custom.js +28 -0
- package/dist/server/auth/providers/custom.js.map +1 -0
- package/dist/server/auth/providers/custom.test.d.ts +1 -0
- package/dist/server/auth/providers/custom.test.js +91 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +14 -0
- package/dist/server/auth/providers/descope.js +14 -0
- package/dist/server/auth/providers/descope.js.map +1 -0
- package/dist/server/auth/providers/descope.test.d.ts +1 -0
- package/dist/server/auth/providers/descope.test.js +35 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +12 -0
- package/dist/server/auth/providers/stytch.js +17 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/stytch.test.d.ts +1 -0
- package/dist/server/auth/providers/stytch.test.js +33 -0
- package/dist/server/auth/providers/stytch.test.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +11 -0
- package/dist/server/auth/providers/workos.js +16 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/providers/workos.test.d.ts +1 -0
- package/dist/server/auth/providers/workos.test.js +41 -0
- package/dist/server/auth/providers/workos.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +4 -0
- package/dist/server/auth/setup.js +51 -0
- package/dist/server/auth/setup.js.map +1 -0
- package/dist/server/auth/setup.test.d.ts +1 -0
- package/dist/server/auth/setup.test.js +185 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +12 -0
- package/dist/server/auth/verify.js +38 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +100 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +40 -0
- package/dist/server/content-helpers.js +33 -0
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/express.test.js +61 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +9 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.d.ts +16 -3
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/requestOrigin.d.ts +7 -0
- package/dist/server/requestOrigin.js +25 -0
- package/dist/server/requestOrigin.js.map +1 -0
- package/dist/server/server.d.ts +213 -6
- package/dist/server/server.js +235 -62
- package/dist/server/server.js.map +1 -1
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +88 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/test/view.test.js +45 -0
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +6 -1
- package/dist/web/bridges/apps-sdk/adaptor.js +22 -1
- package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.d.ts +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js +1 -0
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/types.d.ts +2 -0
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +11 -0
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.d.ts +7 -0
- package/dist/web/bridges/get-adaptor.js +7 -0
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +6 -1
- package/dist/web/bridges/mcp-app/adaptor.js +20 -0
- package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +4 -2
- package/dist/web/bridges/mcp-app/bridge.js +23 -1
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +12 -0
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js +144 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +94 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/use-host-context.d.ts +5 -0
- package/dist/web/bridges/use-host-context.js +5 -0
- package/dist/web/bridges/use-host-context.js.map +1 -1
- package/dist/web/create-store.d.ts +26 -0
- package/dist/web/create-store.js +26 -0
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/data-llm.d.ts +33 -0
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -0
- package/dist/web/generate-helpers.js +2 -0
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +4 -2
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/hooks/index.d.ts +4 -0
- package/dist/web/hooks/index.js +4 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +6 -2
- package/dist/web/hooks/test/utils.js +13 -2
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +27 -2
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +20 -0
- package/dist/web/hooks/use-display-mode.js +20 -0
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +5 -0
- package/dist/web/hooks/use-download.js +8 -0
- package/dist/web/hooks/use-download.js.map +1 -0
- package/dist/web/hooks/use-download.test.d.ts +1 -0
- package/dist/web/hooks/use-download.test.js +95 -0
- package/dist/web/hooks/use-download.test.js.map +1 -0
- package/dist/web/hooks/use-files.d.ts +32 -0
- package/dist/web/hooks/use-files.js +32 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +17 -0
- package/dist/web/hooks/use-open-external.js +16 -0
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-register-view-tool.d.ts +38 -0
- package/dist/web/hooks/use-register-view-tool.js +50 -0
- package/dist/web/hooks/use-register-view-tool.js.map +1 -0
- package/dist/web/hooks/use-request-close.d.ts +16 -0
- package/dist/web/hooks/use-request-close.js +21 -0
- package/dist/web/hooks/use-request-close.js.map +1 -0
- package/dist/web/hooks/use-request-close.test.d.ts +1 -0
- package/dist/web/hooks/use-request-close.test.js +52 -0
- package/dist/web/hooks/use-request-close.test.js.map +1 -0
- package/dist/web/hooks/use-request-modal.d.ts +16 -1
- package/dist/web/hooks/use-request-modal.js +16 -1
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +20 -0
- package/dist/web/hooks/use-request-size.js +24 -0
- package/dist/web/hooks/use-request-size.js.map +1 -0
- package/dist/web/hooks/use-request-size.test.d.ts +1 -0
- package/dist/web/hooks/use-request-size.test.js +65 -0
- package/dist/web/hooks/use-request-size.test.js.map +1 -0
- package/dist/web/hooks/use-send-follow-up-message.d.ts +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js +17 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.d.ts +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js +17 -0
- package/dist/web/hooks/use-set-open-in-app-url.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +53 -2
- package/dist/web/hooks/use-tool-info.js +30 -7
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +11 -29
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +5 -5
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +2 -0
- package/dist/web/hooks/use-user.js +2 -0
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/mount-view.d.ts +19 -0
- package/dist/web/mount-view.js +19 -0
- package/dist/web/mount-view.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +28 -0
- package/dist/web/plugin/plugin.js +33 -0
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +10 -3
|
@@ -1,22 +1,45 @@
|
|
|
1
1
|
import { useHostContext } from "../bridges/index.js";
|
|
2
|
-
function deriveStatus(
|
|
3
|
-
if (input === null) {
|
|
4
|
-
return "idle";
|
|
5
|
-
}
|
|
2
|
+
function deriveStatus(output, responseMetadata) {
|
|
6
3
|
if (output === null && responseMetadata === null) {
|
|
7
4
|
return "pending";
|
|
8
5
|
}
|
|
9
6
|
return "success";
|
|
10
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Access the tool invocation that produced the current view: its `input`,
|
|
10
|
+
* resulting `output`, and `responseMetadata`. The shape evolves as the tool
|
|
11
|
+
* runs (pending → success), exposed through {@link ToolState}.
|
|
12
|
+
*
|
|
13
|
+
* For full input/output typing per tool name, prefer the typed `useToolInfo`
|
|
14
|
+
* returned by {@link generateHelpers} over the generic form.
|
|
15
|
+
*
|
|
16
|
+
* @typeParam TS - Optional partial shape `{ input, output, responseMetadata }`
|
|
17
|
+
* to refine each field's type. When omitted, each typed field resolves to
|
|
18
|
+
* `never` — pass an explicit shape or use the typed helper from
|
|
19
|
+
* {@link generateHelpers} to get usable types.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* const { isSuccess, input, output } = useToolInfo<{
|
|
24
|
+
* input: { query: string };
|
|
25
|
+
* output: { results: Result[] };
|
|
26
|
+
* }>();
|
|
27
|
+
*
|
|
28
|
+
* if (!isSuccess || !output) return <Skeleton />;
|
|
29
|
+
* return <Results items={output.results} />;
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @see https://docs.skybridge.tech/api-reference/use-tool-info
|
|
33
|
+
*/
|
|
11
34
|
export function useToolInfo() {
|
|
12
35
|
const input = useHostContext("toolInput");
|
|
13
36
|
const output = useHostContext("toolOutput");
|
|
14
37
|
const responseMetadata = useHostContext("toolResponseMetadata");
|
|
15
|
-
const status = deriveStatus(
|
|
38
|
+
const status = deriveStatus(output, responseMetadata);
|
|
16
39
|
return {
|
|
17
|
-
input,
|
|
40
|
+
input: input ?? undefined,
|
|
18
41
|
status,
|
|
19
|
-
isIdle:
|
|
42
|
+
isIdle: false,
|
|
20
43
|
isPending: status === "pending",
|
|
21
44
|
isSuccess: status === "success",
|
|
22
45
|
output,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-info.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-tool-info.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA6ErD,SAAS,YAAY,CACnB,MAAsC,EACtC,gBAAgD;IAEhD,IAAI,MAAM,KAAK,IAAI,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QACjD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,WAAW;IAGzB,MAAM,KAAK,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,cAAc,CAAC,sBAAsB,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAMtD,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,SAAS;QACzB,MAAM;QACN,MAAM,EAAE,KAAK;QACb,SAAS,EAAE,MAAM,KAAK,SAAS;QAC/B,SAAS,EAAE,MAAM,KAAK,SAAS;QAC/B,MAAM;QACN,gBAAgB;KACqB,CAAC;AAC1C,CAAC","sourcesContent":["import { useHostContext } from \"../bridges/index.js\";\nimport type { UnknownObject } from \"../types.js\";\n\n/**\n * {@link useToolInfo} state before the tool has been invoked.\n *\n * @deprecated `useToolInfo` no longer returns the idle state — it starts in\n * `\"pending\"` and transitions to `\"success\"`, so `isIdle` is always `false` at\n * runtime. This type is retained in {@link ToolState} for backwards\n * compatibility and will be removed in the next major.\n */\nexport type ToolIdleState = {\n status: \"idle\";\n isIdle: true;\n isPending: false;\n isSuccess: false;\n input: undefined;\n output: undefined;\n responseMetadata: undefined;\n};\n\n/**\n * {@link useToolInfo} state while the tool is executing — `output` is not yet\n * available.\n *\n * `input` is optional: the host may render the view before delivering the\n * tool arguments.\n */\nexport type ToolPendingState<ToolInput extends UnknownObject> = {\n status: \"pending\";\n isIdle: false;\n isPending: true;\n isSuccess: false;\n input: ToolInput | undefined;\n output: undefined;\n responseMetadata: undefined;\n};\n\n/**\n * {@link useToolInfo} state once the tool returned — `output` is available.\n *\n * `input` is optional: the host may not have surfaced the tool arguments by\n * the time `output` arrives.\n */\nexport type ToolSuccessState<\n ToolInput extends UnknownObject,\n ToolOutput extends UnknownObject,\n ToolResponseMetadata extends UnknownObject,\n> = {\n status: \"success\";\n isIdle: false;\n isPending: false;\n isSuccess: true;\n input: ToolInput | undefined;\n output: ToolOutput;\n responseMetadata: ToolResponseMetadata;\n};\n\n/**\n * Discriminated union describing the tool invocation that triggered the\n * current view render. Use `isPending` / `isSuccess` to narrow.\n */\nexport type ToolState<\n ToolInput extends UnknownObject,\n ToolOutput extends UnknownObject,\n ToolResponseMetadata extends UnknownObject,\n> =\n | ToolIdleState\n | ToolPendingState<ToolInput>\n | ToolSuccessState<ToolInput, ToolOutput, ToolResponseMetadata>;\n\ntype ToolSignature = {\n input: UnknownObject;\n output: UnknownObject;\n responseMetadata: UnknownObject;\n};\n\nfunction deriveStatus(\n output: Record<string, unknown> | null,\n responseMetadata: Record<string, unknown> | null,\n): \"pending\" | \"success\" {\n if (output === null && responseMetadata === null) {\n return \"pending\";\n }\n return \"success\";\n}\n\n/**\n * Access the tool invocation that produced the current view: its `input`,\n * resulting `output`, and `responseMetadata`. The shape evolves as the tool\n * runs (pending → success), exposed through {@link ToolState}.\n *\n * For full input/output typing per tool name, prefer the typed `useToolInfo`\n * returned by {@link generateHelpers} over the generic form.\n *\n * @typeParam TS - Optional partial shape `{ input, output, responseMetadata }`\n * to refine each field's type. When omitted, each typed field resolves to\n * `never` — pass an explicit shape or use the typed helper from\n * {@link generateHelpers} to get usable types.\n *\n * @example\n * ```tsx\n * const { isSuccess, input, output } = useToolInfo<{\n * input: { query: string };\n * output: { results: Result[] };\n * }>();\n *\n * if (!isSuccess || !output) return <Skeleton />;\n * return <Results items={output.results} />;\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-tool-info\n */\nexport function useToolInfo<\n TS extends Partial<ToolSignature> = Record<string, never>,\n>() {\n const input = useHostContext(\"toolInput\");\n const output = useHostContext(\"toolOutput\");\n const responseMetadata = useHostContext(\"toolResponseMetadata\");\n\n const status = deriveStatus(output, responseMetadata);\n\n type Input = UnknownObject & TS[\"input\"];\n type Output = UnknownObject & TS[\"output\"];\n type Metadata = UnknownObject & TS[\"responseMetadata\"];\n\n return {\n input: input ?? undefined,\n status,\n isIdle: false,\n isPending: status === \"pending\",\n isSuccess: status === \"success\",\n output,\n responseMetadata,\n } as ToolState<Input, Output, Metadata>;\n}\n"]}
|
|
@@ -11,19 +11,15 @@ test("useToolInfo - TypeScript typing", () => {
|
|
|
11
11
|
});
|
|
12
12
|
test("should correctly type input, output, and responseMetadata with explicit ToolSignature", () => {
|
|
13
13
|
const result = useToolInfo();
|
|
14
|
-
// When
|
|
15
|
-
|
|
16
|
-
expectTypeOf(result.input);
|
|
17
|
-
expectTypeOf(result.output);
|
|
18
|
-
expectTypeOf(result.responseMetadata);
|
|
19
|
-
}
|
|
20
|
-
// When pending, output and responseMetadata should be undefined
|
|
14
|
+
// When pending, input may be undefined (host hasn't delivered args yet,
|
|
15
|
+
// or the tool has no input schema)
|
|
21
16
|
if (result.status === "pending") {
|
|
22
17
|
expectTypeOf(result.input);
|
|
23
18
|
expectTypeOf(result.output);
|
|
24
19
|
expectTypeOf(result.responseMetadata);
|
|
25
20
|
}
|
|
26
|
-
// When success, output and responseMetadata
|
|
21
|
+
// When success, output and responseMetadata are defined; input may still
|
|
22
|
+
// be undefined if the host hasn't surfaced the tool arguments
|
|
27
23
|
if (result.status === "success") {
|
|
28
24
|
expectTypeOf(result.input);
|
|
29
25
|
expectTypeOf(result.output);
|
|
@@ -32,34 +28,18 @@ test("useToolInfo - TypeScript typing", () => {
|
|
|
32
28
|
});
|
|
33
29
|
test("should correctly narrow types based on status discriminated union", () => {
|
|
34
30
|
const result = useToolInfo();
|
|
35
|
-
// Test type narrowing for idle
|
|
36
|
-
if (result.isIdle) {
|
|
37
|
-
expectTypeOf(result.status);
|
|
38
|
-
expectTypeOf(result.isIdle);
|
|
39
|
-
expectTypeOf(result.isPending);
|
|
40
|
-
expectTypeOf(result.isSuccess);
|
|
41
|
-
expectTypeOf(result.input);
|
|
42
|
-
expectTypeOf(result.output);
|
|
43
|
-
expectTypeOf(result.responseMetadata);
|
|
44
|
-
}
|
|
45
31
|
// Test type narrowing for pending
|
|
46
32
|
if (result.isPending) {
|
|
47
33
|
expectTypeOf(result.status);
|
|
48
34
|
expectTypeOf(result.isIdle);
|
|
49
35
|
expectTypeOf(result.isPending);
|
|
50
36
|
expectTypeOf(result.isSuccess);
|
|
37
|
+
expectTypeOf(result.input);
|
|
51
38
|
expectTypeOf(result.output);
|
|
52
39
|
expectTypeOf(result.responseMetadata);
|
|
53
40
|
}
|
|
54
41
|
if (result.isSuccess) {
|
|
55
42
|
expectTypeOf(result.status);
|
|
56
|
-
expectTypeOf(result.isIdle);
|
|
57
|
-
expectTypeOf(result.isPending);
|
|
58
|
-
expectTypeOf(result.isSuccess);
|
|
59
|
-
expectTypeOf(result.output);
|
|
60
|
-
expectTypeOf(result.responseMetadata);
|
|
61
|
-
}
|
|
62
|
-
if (result.status === "idle") {
|
|
63
43
|
expectTypeOf(result.isIdle);
|
|
64
44
|
expectTypeOf(result.isPending);
|
|
65
45
|
expectTypeOf(result.isSuccess);
|
|
@@ -86,22 +66,24 @@ test("useToolInfo - TypeScript typing", () => {
|
|
|
86
66
|
});
|
|
87
67
|
test("should handle partial ToolSignature with only input specified", () => {
|
|
88
68
|
const result = useToolInfo();
|
|
89
|
-
// Input
|
|
90
|
-
|
|
69
|
+
// Input is optional in both states — undefined while args haven't arrived
|
|
70
|
+
// (pending) and for no-input tools (success).
|
|
71
|
+
if (result.status === "pending") {
|
|
91
72
|
expectTypeOf(result.input);
|
|
92
73
|
}
|
|
93
74
|
if (result.status === "success") {
|
|
75
|
+
expectTypeOf(result.input);
|
|
94
76
|
expectTypeOf(result.output);
|
|
95
77
|
expectTypeOf(result.responseMetadata);
|
|
96
78
|
}
|
|
97
79
|
});
|
|
98
80
|
test("should handle ToolSignature with only output specified", () => {
|
|
99
81
|
const result = useToolInfo();
|
|
100
|
-
|
|
101
|
-
if (result.status === "pending" || result.status === "success") {
|
|
82
|
+
if (result.status === "pending") {
|
|
102
83
|
expectTypeOf(result.input);
|
|
103
84
|
}
|
|
104
85
|
if (result.status === "success") {
|
|
86
|
+
expectTypeOf(result.input);
|
|
105
87
|
expectTypeOf(result.output);
|
|
106
88
|
}
|
|
107
89
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-info.test-d.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.test-d.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC3C,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;QAC3E,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;QAE7B,YAAY,CAAiC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5D,YAAY,CAAU,MAAM,CAAC,SAAS,CAAC,CAAC;QACxC,YAAY,CAAU,MAAM,CAAC,SAAS,CAAC,CAAC;QACxC,YAAY,CAAU,MAAM,CAAC,MAAM,CAAC,CAAC;QACrC,YAAY,CAAsC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAKjG,MAAM,MAAM,GAAG,WAAW,EAItB,CAAC;QAEL,
|
|
1
|
+
{"version":3,"file":"use-tool-info.test-d.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.test-d.ts"],"names":[],"mappings":"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","sourcesContent":["import { expectTypeOf, test } from \"vitest\";\nimport { useToolInfo } from \"./use-tool-info.js\";\n\ntest(\"useToolInfo - TypeScript typing\", () => {\n test(\"should have correct types when no generic parameter is provided\", () => {\n const result = useToolInfo();\n\n expectTypeOf<\"idle\" | \"pending\" | \"success\">(result.status);\n expectTypeOf<boolean>(result.isPending);\n expectTypeOf<boolean>(result.isSuccess);\n expectTypeOf<boolean>(result.isIdle);\n expectTypeOf<Record<string, unknown> | undefined>(result.input);\n });\n\n test(\"should correctly type input, output, and responseMetadata with explicit ToolSignature\", () => {\n type TestInput = { name: string; args: { name: string } };\n type TestOutput = { name: string; color: string };\n type TestMetadata = { id: number };\n\n const result = useToolInfo<{\n input: TestInput;\n output: TestOutput;\n responseMetadata: TestMetadata;\n }>();\n\n // When pending, input may be undefined (host hasn't delivered args yet,\n // or the tool has no input schema)\n if (result.status === \"pending\") {\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<undefined>(result.output);\n expectTypeOf<undefined>(result.responseMetadata);\n }\n\n // When success, output and responseMetadata are defined; input may still\n // be undefined if the host hasn't surfaced the tool arguments\n if (result.status === \"success\") {\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<TestOutput>(result.output);\n expectTypeOf<TestMetadata>(result.responseMetadata);\n }\n });\n\n test(\"should correctly narrow types based on status discriminated union\", () => {\n type TestInput = { query: string };\n type TestOutput = { result: string };\n type TestMetadata = { timestamp: number };\n\n const result = useToolInfo<{\n input: TestInput;\n output: TestOutput;\n responseMetadata: TestMetadata;\n }>();\n\n // Test type narrowing for pending\n if (result.isPending) {\n expectTypeOf<\"pending\">(result.status);\n expectTypeOf<false>(result.isIdle);\n expectTypeOf<true>(result.isPending);\n expectTypeOf<false>(result.isSuccess);\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<undefined>(result.output);\n expectTypeOf<undefined>(result.responseMetadata);\n }\n\n if (result.isSuccess) {\n expectTypeOf<\"success\">(result.status);\n expectTypeOf<false>(result.isIdle);\n expectTypeOf<false>(result.isPending);\n expectTypeOf<true>(result.isSuccess);\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<TestOutput>(result.output);\n expectTypeOf<TestMetadata>(result.responseMetadata);\n }\n\n if (result.status === \"pending\") {\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<false>(result.isIdle);\n expectTypeOf<true>(result.isPending);\n expectTypeOf<false>(result.isSuccess);\n expectTypeOf<undefined>(result.output);\n expectTypeOf<undefined>(result.responseMetadata);\n }\n\n if (result.status === \"success\") {\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<false>(result.isIdle);\n expectTypeOf<false>(result.isPending);\n expectTypeOf<true>(result.isSuccess);\n expectTypeOf<TestOutput>(result.output);\n expectTypeOf<TestMetadata>(result.responseMetadata);\n }\n });\n\n test(\"should handle partial ToolSignature with only input specified\", () => {\n type TestInput = { id: number };\n\n const result = useToolInfo<{\n input: TestInput;\n }>();\n\n // Input is optional in both states — undefined while args haven't arrived\n // (pending) and for no-input tools (success).\n if (result.status === \"pending\") {\n expectTypeOf<TestInput | undefined>(result.input);\n }\n\n if (result.status === \"success\") {\n expectTypeOf<TestInput | undefined>(result.input);\n expectTypeOf<Record<string, unknown>>(result.output);\n expectTypeOf<Record<string, unknown>>(result.responseMetadata);\n }\n });\n\n test(\"should handle ToolSignature with only output specified\", () => {\n type TestOutput = { data: string[] };\n\n const result = useToolInfo<{\n output: TestOutput;\n }>();\n\n if (result.status === \"pending\") {\n expectTypeOf<Record<string, unknown> | undefined>(result.input);\n }\n\n if (result.status === \"success\") {\n expectTypeOf<Record<string, unknown> | undefined>(result.input);\n expectTypeOf<TestOutput>(result.output);\n }\n });\n});\n"]}
|
|
@@ -74,15 +74,15 @@ describe("useToolInfo", () => {
|
|
|
74
74
|
McpAppBridge.resetInstance();
|
|
75
75
|
McpAppAdaptor.resetInstance();
|
|
76
76
|
});
|
|
77
|
-
it("should return
|
|
77
|
+
it("should return pending state with undefined input before tool-input notification arrives", async () => {
|
|
78
78
|
const { result } = renderHook(() => useToolInfo());
|
|
79
79
|
await waitFor(() => {
|
|
80
80
|
expect(result.current).toMatchObject({
|
|
81
|
-
status: "
|
|
82
|
-
isIdle:
|
|
83
|
-
isPending:
|
|
81
|
+
status: "pending",
|
|
82
|
+
isIdle: false,
|
|
83
|
+
isPending: true,
|
|
84
84
|
isSuccess: false,
|
|
85
|
-
input:
|
|
85
|
+
input: undefined,
|
|
86
86
|
output: null,
|
|
87
87
|
responseMetadata: null,
|
|
88
88
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-tool-info.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"use-tool-info.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-tool-info.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { act, fireEvent, renderHook, waitFor } from \"@testing-library/react\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n type AppsSdkContext,\n SET_GLOBALS_EVENT_TYPE,\n SetGlobalsEvent,\n} from \"../bridges/apps-sdk/index.js\";\nimport { McpAppAdaptor, McpAppBridge } from \"../bridges/mcp-app/index.js\";\nimport {\n fireToolInputNotification,\n fireToolResultNotification,\n getMcpAppHostPostMessageMock,\n MockResizeObserver,\n} from \"./test/utils.js\";\nimport { useToolInfo } from \"./use-tool-info.js\";\n\ndescribe(\"useToolInfo\", () => {\n describe(\"apps-sdk host\", () => {\n let OpenaiMock: Pick<\n AppsSdkContext,\n \"toolInput\" | \"toolOutput\" | \"toolResponseMetadata\"\n >;\n\n beforeEach(() => {\n OpenaiMock = {\n toolInput: { name: \"pokemon\", args: { name: \"pikachu\" } },\n toolOutput: null,\n toolResponseMetadata: null,\n };\n vi.stubGlobal(\"openai\", OpenaiMock);\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n });\n\n it(\"should return toolInput on initial mount window.openai\", () => {\n const { result } = renderHook(() => useToolInfo());\n\n expect(result.current).toMatchObject({\n input: { name: \"pokemon\", args: { name: \"pikachu\" } },\n status: \"pending\",\n isIdle: false,\n isPending: true,\n isSuccess: false,\n });\n });\n\n it(\"should eventually return tool output and response metadata once tool call completes\", async () => {\n const toolOutput = {\n name: \"pikachu\",\n color: \"yellow\",\n description:\n \"When several of these POKéMON gather, their\\felectricity could build and cause lightning storms.\",\n };\n const toolResponseMetadata = { id: 12 };\n const { result } = renderHook(() => useToolInfo());\n\n act(() => {\n OpenaiMock.toolOutput = toolOutput;\n OpenaiMock.toolResponseMetadata = toolResponseMetadata;\n fireEvent(\n window,\n new SetGlobalsEvent(SET_GLOBALS_EVENT_TYPE, {\n detail: {\n globals: {\n toolOutput,\n toolResponseMetadata,\n },\n },\n }),\n );\n });\n\n await waitFor(() => {\n expect(result.current).toMatchObject({\n status: \"success\",\n isIdle: false,\n isPending: false,\n isSuccess: true,\n output: toolOutput,\n responseMetadata: toolResponseMetadata,\n });\n });\n });\n });\n\n describe(\"mcp-app host\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"parent\", { postMessage: getMcpAppHostPostMessageMock() });\n vi.stubGlobal(\"skybridge\", { hostType: \"mcp-app\" });\n vi.stubGlobal(\"ResizeObserver\", MockResizeObserver);\n });\n\n afterEach(async () => {\n vi.unstubAllGlobals();\n vi.resetAllMocks();\n McpAppBridge.resetInstance();\n McpAppAdaptor.resetInstance();\n });\n\n it(\"should return pending state with undefined input before tool-input notification arrives\", async () => {\n const { result } = renderHook(() => useToolInfo());\n\n await waitFor(() => {\n expect(result.current).toMatchObject({\n status: \"pending\",\n isIdle: false,\n isPending: true,\n isSuccess: false,\n input: undefined,\n output: null,\n responseMetadata: null,\n });\n });\n });\n\n it(\"should return pending state with tool input from tool-input notification\", async () => {\n const { result } = renderHook(() => useToolInfo());\n\n act(() => {\n fireToolInputNotification({ name: \"pokemon\", query: \"pikachu\" });\n });\n\n await waitFor(() => {\n expect(result.current).toMatchObject({\n status: \"pending\",\n isIdle: false,\n isPending: true,\n isSuccess: false,\n input: { name: \"pokemon\", query: \"pikachu\" },\n });\n });\n });\n\n it(\"should return success state with output from tool-result notification\", async () => {\n const { result } = renderHook(() => useToolInfo());\n\n act(() => {\n fireToolInputNotification({ name: \"pokemon\", query: \"pikachu\" });\n fireToolResultNotification({\n content: [{ type: \"text\", text: \"Pikachu data\" }],\n structuredContent: { name: \"pikachu\", color: \"yellow\" },\n _meta: { requestId: \"123\" },\n });\n });\n\n await waitFor(() => {\n expect(result.current).toMatchObject({\n status: \"success\",\n isIdle: false,\n isPending: false,\n isSuccess: true,\n input: { name: \"pokemon\", query: \"pikachu\" },\n output: { name: \"pikachu\", color: \"yellow\" },\n responseMetadata: { requestId: \"123\" },\n });\n });\n });\n });\n});\n"]}
|
|
@@ -26,6 +26,8 @@ function normalizeLocale(locale) {
|
|
|
26
26
|
* // Access device type
|
|
27
27
|
* const isMobile = userAgent.device.type === "mobile";
|
|
28
28
|
* ```
|
|
29
|
+
*
|
|
30
|
+
* @see https://docs.skybridge.tech/api-reference/use-user
|
|
29
31
|
*/
|
|
30
32
|
export function useUser() {
|
|
31
33
|
const rawLocale = useHostContext("locale");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-user.js","sourceRoot":"","sources":["../../../src/web/hooks/use-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAOrE,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"use-user.js","sourceRoot":"","sources":["../../../src/web/hooks/use-user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAOrE,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,cAAc,CAAC;IACxB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,SAAS,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAE9C,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC;AAC3D,CAAC","sourcesContent":["import { type UserAgent, useHostContext } from \"../bridges/index.js\";\n\nexport type UserState = {\n locale: string;\n userAgent: UserAgent;\n};\n\nconst DEFAULT_LOCALE = \"en-US\";\n\n/**\n * Normalizes a locale string to canonical BCP 47 format using {@link Intl.Locale}.\n *\n * Handles underscored identifiers returned by the ChatGPT mobile app (e.g. \"fr_FR\" → \"fr-FR\"),\n * incorrect casing (e.g. \"en-us\" → \"en-US\"), and complex subtags (e.g. \"zh_Hans_CN\" → \"zh-Hans-CN\").\n * Falls back to \"en-US\" if the locale is invalid.\n */\nfunction normalizeLocale(locale: string): string {\n try {\n return new Intl.Locale(locale.replace(/_/g, \"-\")).toString();\n } catch {\n return DEFAULT_LOCALE;\n }\n}\n\n/**\n * Hook for accessing session-stable user information.\n * These values are set once at initialization and do not change during the session.\n *\n * @example\n * ```tsx\n * const { locale, userAgent } = useUser();\n *\n * // Access device type\n * const isMobile = userAgent.device.type === \"mobile\";\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-user\n */\nexport function useUser(): UserState {\n const rawLocale = useHostContext(\"locale\");\n const userAgent = useHostContext(\"userAgent\");\n\n return { locale: normalizeLocale(rawLocale), userAgent };\n}\n"]}
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
import { type SetStateAction } from "react";
|
|
2
2
|
import type { UnknownObject } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Persist a piece of UI state on the host, so it survives view remounts and
|
|
5
|
+
* is restored on subsequent renders of the same tool invocation.
|
|
6
|
+
*
|
|
7
|
+
* Returns a `[state, setState]` pair with the same ergonomics as
|
|
8
|
+
* `useState`. State is filtered to strip Skybridge-internal context fields
|
|
9
|
+
* (see {@link DataLLM}) before being returned to your component.
|
|
10
|
+
*
|
|
11
|
+
* Provide a `defaultState` (value or lazy initializer) to get a non-nullable
|
|
12
|
+
* tuple; omit it for `T | null`.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T - Shape of the persisted state. Must be a plain object.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const [filters, setFilters] = useViewState({ sort: "newest", page: 1 });
|
|
19
|
+
* setFilters((f) => ({ ...f, page: f.page + 1 }));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see https://docs.skybridge.tech/api-reference/use-view-state
|
|
23
|
+
*/
|
|
3
24
|
export declare function useViewState<T extends UnknownObject>(defaultState: T | (() => T)): readonly [T, (state: SetStateAction<T>) => void];
|
|
4
25
|
export declare function useViewState<T extends UnknownObject>(defaultState?: T | (() => T | null) | null): readonly [T | null, (state: SetStateAction<T | null>) => void];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-view-state.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-view-state.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA8B3E,MAAM,UAAU,YAAY,CAC1B,YAA0C;IAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAa,CAAC;IAEpE,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAW,GAAG,EAAE;QACzD,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;YACjC,OAAO,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,OAAO,YAAY,KAAK,UAAU;YACvC,CAAC,CAAC,YAAY,EAAE;YAChB,CAAC,CAAC,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAE1B,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAA+B,EAAE,EAAE;QAClC,aAAa,CAAC,CAAC,SAAS,EAAE,EAAE;YAC1B,MAAM,QAAQ,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YACxE,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAE/C,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YACnC,CAAC;YAED,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,EACD,CAAC,OAAO,CAAC,CACV,CAAC;IAEF,OAAO,CAAC,SAAS,EAAE,YAAY,CAAU,CAAC;AAC5C,CAAC","sourcesContent":["import { type SetStateAction, useCallback, useEffect, useState } from \"react\";\nimport { getAdaptor, useHostContext } from \"../bridges/index.js\";\nimport { filterViewContext, injectViewContext } from \"../helpers/state.js\";\nimport type { UnknownObject } from \"../types.js\";\n\n/**\n * Persist a piece of UI state on the host, so it survives view remounts and\n * is restored on subsequent renders of the same tool invocation.\n *\n * Returns a `[state, setState]` pair with the same ergonomics as\n * `useState`. State is filtered to strip Skybridge-internal context fields\n * (see {@link DataLLM}) before being returned to your component.\n *\n * Provide a `defaultState` (value or lazy initializer) to get a non-nullable\n * tuple; omit it for `T | null`.\n *\n * @typeParam T - Shape of the persisted state. Must be a plain object.\n *\n * @example\n * ```tsx\n * const [filters, setFilters] = useViewState({ sort: \"newest\", page: 1 });\n * setFilters((f) => ({ ...f, page: f.page + 1 }));\n * ```\n *\n * @see https://docs.skybridge.tech/api-reference/use-view-state\n */\nexport function useViewState<T extends UnknownObject>(\n defaultState: T | (() => T),\n): readonly [T, (state: SetStateAction<T>) => void];\nexport function useViewState<T extends UnknownObject>(\n defaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void];\nexport function useViewState<T extends UnknownObject>(\n defaultState?: T | (() => T | null) | null,\n): readonly [T | null, (state: SetStateAction<T | null>) => void] {\n const adaptor = getAdaptor();\n const viewStateFromBridge = useHostContext(\"viewState\") as T | null;\n\n const [viewState, _setViewState] = useState<T | null>(() => {\n if (viewStateFromBridge !== null) {\n return filterViewContext(viewStateFromBridge);\n }\n\n return typeof defaultState === \"function\"\n ? defaultState()\n : (defaultState ?? null);\n });\n\n useEffect(() => {\n if (viewStateFromBridge !== null) {\n _setViewState(filterViewContext(viewStateFromBridge));\n }\n }, [viewStateFromBridge]);\n\n const setViewState = useCallback(\n (state: SetStateAction<T | null>) => {\n _setViewState((prevState) => {\n const newState = typeof state === \"function\" ? state(prevState) : state;\n const stateToSet = injectViewContext(newState);\n\n if (stateToSet !== null) {\n adaptor.setViewState(stateToSet);\n }\n\n return filterViewContext(stateToSet);\n });\n },\n [adaptor],\n );\n\n return [viewState, setViewState] as const;\n}\n"]}
|
package/dist/web/mount-view.d.ts
CHANGED
|
@@ -1 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount a view's root React component into `#root`. Each view file's entry
|
|
3
|
+
* point should call this exactly once.
|
|
4
|
+
*
|
|
5
|
+
* Wraps the component in `StrictMode`, applies host-specific providers
|
|
6
|
+
* automatically (e.g. modal support for MCP Apps), and installs the dev-mode
|
|
7
|
+
* logging proxy for `window.openai` calls.
|
|
8
|
+
*
|
|
9
|
+
* @param component - Your root React element (already constructed, e.g. `<App />`).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* // src/views/search.tsx
|
|
14
|
+
* import { mountView } from "skybridge/web";
|
|
15
|
+
* import { App } from "./App";
|
|
16
|
+
*
|
|
17
|
+
* mountView(<App />);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
1
20
|
export declare const mountView: (component: React.ReactNode) => void;
|
package/dist/web/mount-view.js
CHANGED
|
@@ -3,6 +3,25 @@ import { createElement, StrictMode } from "react";
|
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
import { installOpenAILoggingProxy } from "./proxy.js";
|
|
5
5
|
let rootInstance = null;
|
|
6
|
+
/**
|
|
7
|
+
* Mount a view's root React component into `#root`. Each view file's entry
|
|
8
|
+
* point should call this exactly once.
|
|
9
|
+
*
|
|
10
|
+
* Wraps the component in `StrictMode`, applies host-specific providers
|
|
11
|
+
* automatically (e.g. modal support for MCP Apps), and installs the dev-mode
|
|
12
|
+
* logging proxy for `window.openai` calls.
|
|
13
|
+
*
|
|
14
|
+
* @param component - Your root React element (already constructed, e.g. `<App />`).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* // src/views/search.tsx
|
|
19
|
+
* import { mountView } from "skybridge/web";
|
|
20
|
+
* import { App } from "./App";
|
|
21
|
+
*
|
|
22
|
+
* mountView(<App />);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
6
25
|
export const mountView = (component) => {
|
|
7
26
|
const rootElement = document.getElementById("root");
|
|
8
27
|
if (!rootElement) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount-view.js","sourceRoot":"","sources":["../../src/web/mount-view.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,SAAS,GAAG,CAAC,SAA0B,EAAE,EAAE;IACtD,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,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;IAE5C,CAAC,KAAK,IAAI,EAAE;QACV,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;YACzE,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,EAAE,CAAC;AACP,CAAC,CAAC","sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { createElement, StrictMode } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { installOpenAILoggingProxy } from \"./proxy.js\";\n\nlet rootInstance: Root | null = null;\n\nexport const mountView = (component: React.ReactNode) => {\n const rootElement = document.getElementById(\"root\");\n if (!rootElement) {\n throw new Error(\"Root element not found\");\n }\n\n if (!rootInstance) {\n rootInstance = createRoot(rootElement);\n }\n\n if (import.meta.env.DEV) {\n installOpenAILoggingProxy();\n }\n\n const hostType = window.skybridge?.hostType;\n\n (async () => {\n let app = component;\n if (hostType === \"mcp-app\") {\n const { ModalProvider } = await import(\"./components/modal-provider.js\");\n app = createElement(ModalProvider, null, component);\n }\n rootInstance.render(createElement(StrictMode, null, app));\n })();\n};\n"]}
|
|
1
|
+
{"version":3,"file":"mount-view.js","sourceRoot":"","sources":["../../src/web/mount-view.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;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAA0B,EAAE,EAAE;IACtD,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,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC;IAE5C,CAAC,KAAK,IAAI,EAAE;QACV,IAAI,GAAG,GAAG,SAAS,CAAC;QACpB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gCAAgC,CAAC,CAAC;YACzE,GAAG,GAAG,aAAa,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,EAAE,CAAC;AACP,CAAC,CAAC","sourcesContent":["/// <reference types=\"vite/client\" />\n\nimport { createElement, StrictMode } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { installOpenAILoggingProxy } from \"./proxy.js\";\n\nlet rootInstance: Root | null = null;\n\n/**\n * Mount a view's root React component into `#root`. Each view file's entry\n * point should call this exactly once.\n *\n * Wraps the component in `StrictMode`, applies host-specific providers\n * automatically (e.g. modal support for MCP Apps), and installs the dev-mode\n * logging proxy for `window.openai` calls.\n *\n * @param component - Your root React element (already constructed, e.g. `<App />`).\n *\n * @example\n * ```tsx\n * // src/views/search.tsx\n * import { mountView } from \"skybridge/web\";\n * import { App } from \"./App\";\n *\n * mountView(<App />);\n * ```\n */\nexport const mountView = (component: React.ReactNode) => {\n const rootElement = document.getElementById(\"root\");\n if (!rootElement) {\n throw new Error(\"Root element not found\");\n }\n\n if (!rootInstance) {\n rootInstance = createRoot(rootElement);\n }\n\n if (import.meta.env.DEV) {\n installOpenAILoggingProxy();\n }\n\n const hostType = window.skybridge?.hostType;\n\n (async () => {\n let app = component;\n if (hostType === \"mcp-app\") {\n const { ModalProvider } = await import(\"./components/modal-provider.js\");\n app = createElement(ModalProvider, null, component);\n }\n rootInstance.render(createElement(StrictMode, null, app));\n })();\n};\n"]}
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
|
+
/** Options for the {@link skybridge} Vite plugin. */
|
|
2
3
|
export interface SkybridgePluginOptions {
|
|
4
|
+
/** Directory scanned for view modules. Defaults to `"src/views"`. */
|
|
3
5
|
viewsDir?: string;
|
|
4
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Vite plugin that wires a Skybridge project's view files into Vite.
|
|
9
|
+
*
|
|
10
|
+
* For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the
|
|
11
|
+
* plugin:
|
|
12
|
+
* - exposes a virtual entry that calls {@link mountView} with the view's
|
|
13
|
+
* default export,
|
|
14
|
+
* - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so
|
|
15
|
+
* {@link ViewName} narrows to the actual view names,
|
|
16
|
+
* - rewrites `<DataLLM>` JSX so the host can extract its content,
|
|
17
|
+
* - warns in dev if a view file is missing a default export.
|
|
18
|
+
*
|
|
19
|
+
* Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* // vite.config.ts
|
|
24
|
+
* import { defineConfig } from "vite";
|
|
25
|
+
* import react from "@vitejs/plugin-react";
|
|
26
|
+
* import { skybridge } from "skybridge/vite";
|
|
27
|
+
*
|
|
28
|
+
* export default defineConfig({
|
|
29
|
+
* plugins: [react(), skybridge({ viewsDir: "src/views" })],
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
5
33
|
export declare function skybridge(options?: SkybridgePluginOptions): Plugin;
|
|
@@ -17,6 +17,32 @@ function getViewEntryPattern(viewsDir) {
|
|
|
17
17
|
const escaped = viewsDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18
18
|
return new RegExp(`${escaped}\\/(?:[^/]+\\.(?:jsx|tsx)|[^/]+\\/index\\.(?:tsx|jsx))(?:\\?.*)?$`);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Vite plugin that wires a Skybridge project's view files into Vite.
|
|
22
|
+
*
|
|
23
|
+
* For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the
|
|
24
|
+
* plugin:
|
|
25
|
+
* - exposes a virtual entry that calls {@link mountView} with the view's
|
|
26
|
+
* default export,
|
|
27
|
+
* - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so
|
|
28
|
+
* {@link ViewName} narrows to the actual view names,
|
|
29
|
+
* - rewrites `<DataLLM>` JSX so the host can extract its content,
|
|
30
|
+
* - warns in dev if a view file is missing a default export.
|
|
31
|
+
*
|
|
32
|
+
* Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* // vite.config.ts
|
|
37
|
+
* import { defineConfig } from "vite";
|
|
38
|
+
* import react from "@vitejs/plugin-react";
|
|
39
|
+
* import { skybridge } from "skybridge/vite";
|
|
40
|
+
*
|
|
41
|
+
* export default defineConfig({
|
|
42
|
+
* plugins: [react(), skybridge({ viewsDir: "src/views" })],
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
20
46
|
export function skybridge(options) {
|
|
21
47
|
const rawViewsDir = options?.viewsDir ?? "src/views";
|
|
22
48
|
let resolvedViewsDir;
|
|
@@ -43,6 +69,13 @@ export function skybridge(options) {
|
|
|
43
69
|
}
|
|
44
70
|
return {
|
|
45
71
|
base: "/assets",
|
|
72
|
+
// Fixes "Invalid hook call" on createStore by forcing a single
|
|
73
|
+
// copy of React. Under pnpm's isolated node_modules, zustand
|
|
74
|
+
// inside `skybridge` resolves React from skybridge's own
|
|
75
|
+
// dependencies while the host app loads its own copy
|
|
76
|
+
resolve: {
|
|
77
|
+
dedupe: ["react", "react-dom"],
|
|
78
|
+
},
|
|
46
79
|
build: {
|
|
47
80
|
outDir: "dist/assets",
|
|
48
81
|
emptyOutDir: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n assertUniqueViewNames,\n type DiscoveredView,\n discoverViewsSync,\n scanViewsSync,\n writeViewsDts,\n} from \"./scan-views.js\";\nimport { transform as dataLlmTransform } from \"./transform-data-llm.js\";\nimport { hasDefaultExport } from \"./validate-view.js\";\n\nconst VIRTUAL_PREFIX = \"/_skybridge/view/\";\nconst VIRTUAL_MODULE_PREFIX = \"\\0skybridge:view:\";\n\nexport interface SkybridgePluginOptions {\n viewsDir?: string;\n}\n\nfunction buildVirtualEntry(viewFilePath: string): string {\n const normalized = viewFilePath.replace(/\\\\/g, \"/\");\n return [\n `import { mountView } from \"skybridge/web\";`,\n `import Component from \"${normalized}\";`,\n `import { createElement } from \"react\";`,\n `mountView(createElement(Component));`,\n ].join(\"\\n\");\n}\n\nfunction getViewEntryPattern(viewsDir: string): RegExp {\n const escaped = viewsDir.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(\n `${escaped}\\\\/(?:[^/]+\\\\.(?:jsx|tsx)|[^/]+\\\\/index\\\\.(?:tsx|jsx))(?:\\\\?.*)?$`,\n );\n}\n\nexport function skybridge(options?: SkybridgePluginOptions): Plugin {\n const rawViewsDir = options?.viewsDir ?? \"src/views\";\n let resolvedViewsDir: string;\n let projectRoot: string;\n let viewMap = new Map<string, DiscoveredView>();\n let viewEntryPattern: RegExp;\n\n return {\n name: \"skybridge\",\n enforce: \"pre\",\n // Read by `skybridge build` to resolve viewsDir before `tsc -b` runs.\n api: { viewsDir: rawViewsDir },\n\n config(config) {\n projectRoot = config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(projectRoot, rawViewsDir);\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n\n const views = discoverViewsSync(resolvedViewsDir);\n viewMap = new Map(views.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, views);\n\n const input: Record<string, string> = {};\n for (const view of views) {\n input[view.name] = `${VIRTUAL_PREFIX}${view.name}`;\n }\n\n return {\n base: \"/assets\",\n build: {\n outDir: \"dist/assets\",\n emptyOutDir: true,\n manifest: true,\n minify: true,\n cssCodeSplit: false,\n rollupOptions: {\n input,\n },\n },\n // Pre-bundle view deps at startup so the first tool invocation\n // doesn't hit Vite's on-demand re-optimization path (which sends\n // `full-reload` over HMR — in our iframe flow the parent host\n // can't honour a reload, and the view silently never mounts).\n optimizeDeps: {\n // Scan view files so transitive user deps (zod, tailwind, etc.)\n // get pre-bundled at startup.\n entries: [\n `${resolvedViewsDir}/*.{tsx,jsx}`,\n `${resolvedViewsDir}/*/index.{tsx,jsx}`,\n ],\n include: [\"react\", \"react-dom/client\", \"react/jsx-runtime\"],\n exclude: [\"skybridge/web\"],\n },\n experimental: {\n renderBuiltUrl: (filename) => {\n return {\n runtime: `window.skybridge.serverUrl + \"/assets/${filename}\"`,\n };\n },\n },\n };\n },\n\n resolveId(id) {\n if (id.startsWith(VIRTUAL_PREFIX)) {\n const name = id.slice(VIRTUAL_PREFIX.length);\n if (viewMap.has(name)) {\n return `${VIRTUAL_MODULE_PREFIX}${name}`;\n }\n }\n return null;\n },\n\n load(id) {\n if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {\n const name = id.slice(VIRTUAL_MODULE_PREFIX.length);\n const view = viewMap.get(name);\n if (view) {\n return buildVirtualEntry(view.filePath);\n }\n }\n return null;\n },\n\n configureServer(server: ViteDevServer) {\n if (!resolvedViewsDir) {\n const root = server.config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(root, rawViewsDir);\n projectRoot = root;\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n }\n\n server.watcher.add(resolvedViewsDir);\n // Track which view files we've already warned about so a rescan\n // triggered by an unrelated edit doesn't re-emit the same warning.\n let knownInvalid = new Set<string>();\n const rescan = () => {\n try {\n // Surface broken view files. Without this, files lacking a\n // default export are silently dropped from the input and the\n // user has no idea why their widget never mounts.\n const { valid, invalid } = scanViewsSync(resolvedViewsDir);\n const nextInvalid = new Set(invalid.map((v) => v.filePath));\n\n for (const filePath of nextInvalid) {\n if (!knownInvalid.has(filePath)) {\n server.config.logger.warn(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" is missing a default export — it won't be served until fixed.`,\n );\n }\n }\n for (const filePath of knownInvalid) {\n if (!nextInvalid.has(filePath)) {\n server.config.logger.info(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" resolved.`,\n );\n }\n }\n knownInvalid = nextInvalid;\n\n assertUniqueViewNames(valid);\n viewMap = new Map(valid.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, valid);\n } catch (err) {\n // assertUniqueViewNames throws on duplicate view names. Catch so\n // chokidar's listener chain doesn't surface it as unhandled and\n // crash the dev server — previous viewMap stays active until\n // the user fixes the conflict.\n const message = err instanceof Error ? err.message : String(err);\n server.config.logger.error(\n `[skybridge] view rescan failed: ${message}`,\n );\n }\n };\n\n // Initial scan emits warnings for broken files that exist at startup.\n rescan();\n server.watcher.on(\"add\", rescan);\n server.watcher.on(\"change\", rescan);\n server.watcher.on(\"unlink\", rescan);\n },\n\n async transform(code, id) {\n if (viewEntryPattern?.test(id) && !hasDefaultExport(code, id)) {\n this.warn(\n `View file \"${id.split(\"/\").pop()}\" is missing a default export.`,\n );\n }\n\n return await dataLlmTransform(code, id);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport type { Plugin, ViteDevServer } from \"vite\";\nimport {\n assertUniqueViewNames,\n type DiscoveredView,\n discoverViewsSync,\n scanViewsSync,\n writeViewsDts,\n} from \"./scan-views.js\";\nimport { transform as dataLlmTransform } from \"./transform-data-llm.js\";\nimport { hasDefaultExport } from \"./validate-view.js\";\n\nconst VIRTUAL_PREFIX = \"/_skybridge/view/\";\nconst VIRTUAL_MODULE_PREFIX = \"\\0skybridge:view:\";\n\n/** Options for the {@link skybridge} Vite plugin. */\nexport interface SkybridgePluginOptions {\n /** Directory scanned for view modules. Defaults to `\"src/views\"`. */\n viewsDir?: string;\n}\n\nfunction buildVirtualEntry(viewFilePath: string): string {\n const normalized = viewFilePath.replace(/\\\\/g, \"/\");\n return [\n `import { mountView } from \"skybridge/web\";`,\n `import Component from \"${normalized}\";`,\n `import { createElement } from \"react\";`,\n `mountView(createElement(Component));`,\n ].join(\"\\n\");\n}\n\nfunction getViewEntryPattern(viewsDir: string): RegExp {\n const escaped = viewsDir.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(\n `${escaped}\\\\/(?:[^/]+\\\\.(?:jsx|tsx)|[^/]+\\\\/index\\\\.(?:tsx|jsx))(?:\\\\?.*)?$`,\n );\n}\n\n/**\n * Vite plugin that wires a Skybridge project's view files into Vite.\n *\n * For each `.tsx` / `.jsx` file in `viewsDir` with a default export, the\n * plugin:\n * - exposes a virtual entry that calls {@link mountView} with the view's\n * default export,\n * - generates `.skybridge/views.d.ts` to augment {@link ViewNameRegistry} so\n * {@link ViewName} narrows to the actual view names,\n * - rewrites `<DataLLM>` JSX so the host can extract its content,\n * - warns in dev if a view file is missing a default export.\n *\n * Add it to your `vite.config.ts` alongside `@vitejs/plugin-react`.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from \"vite\";\n * import react from \"@vitejs/plugin-react\";\n * import { skybridge } from \"skybridge/vite\";\n *\n * export default defineConfig({\n * plugins: [react(), skybridge({ viewsDir: \"src/views\" })],\n * });\n * ```\n */\nexport function skybridge(options?: SkybridgePluginOptions): Plugin {\n const rawViewsDir = options?.viewsDir ?? \"src/views\";\n let resolvedViewsDir: string;\n let projectRoot: string;\n let viewMap = new Map<string, DiscoveredView>();\n let viewEntryPattern: RegExp;\n\n return {\n name: \"skybridge\",\n enforce: \"pre\",\n // Read by `skybridge build` to resolve viewsDir before `tsc -b` runs.\n api: { viewsDir: rawViewsDir },\n\n config(config) {\n projectRoot = config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(projectRoot, rawViewsDir);\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n\n const views = discoverViewsSync(resolvedViewsDir);\n viewMap = new Map(views.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, views);\n\n const input: Record<string, string> = {};\n for (const view of views) {\n input[view.name] = `${VIRTUAL_PREFIX}${view.name}`;\n }\n\n return {\n base: \"/assets\",\n // Fixes \"Invalid hook call\" on createStore by forcing a single\n // copy of React. Under pnpm's isolated node_modules, zustand\n // inside `skybridge` resolves React from skybridge's own\n // dependencies while the host app loads its own copy\n resolve: {\n dedupe: [\"react\", \"react-dom\"],\n },\n build: {\n outDir: \"dist/assets\",\n emptyOutDir: true,\n manifest: true,\n minify: true,\n cssCodeSplit: false,\n rollupOptions: {\n input,\n },\n },\n // Pre-bundle view deps at startup so the first tool invocation\n // doesn't hit Vite's on-demand re-optimization path (which sends\n // `full-reload` over HMR — in our iframe flow the parent host\n // can't honour a reload, and the view silently never mounts).\n optimizeDeps: {\n // Scan view files so transitive user deps (zod, tailwind, etc.)\n // get pre-bundled at startup.\n entries: [\n `${resolvedViewsDir}/*.{tsx,jsx}`,\n `${resolvedViewsDir}/*/index.{tsx,jsx}`,\n ],\n include: [\"react\", \"react-dom/client\", \"react/jsx-runtime\"],\n exclude: [\"skybridge/web\"],\n },\n experimental: {\n renderBuiltUrl: (filename) => {\n return {\n runtime: `window.skybridge.serverUrl + \"/assets/${filename}\"`,\n };\n },\n },\n };\n },\n\n resolveId(id) {\n if (id.startsWith(VIRTUAL_PREFIX)) {\n const name = id.slice(VIRTUAL_PREFIX.length);\n if (viewMap.has(name)) {\n return `${VIRTUAL_MODULE_PREFIX}${name}`;\n }\n }\n return null;\n },\n\n load(id) {\n if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {\n const name = id.slice(VIRTUAL_MODULE_PREFIX.length);\n const view = viewMap.get(name);\n if (view) {\n return buildVirtualEntry(view.filePath);\n }\n }\n return null;\n },\n\n configureServer(server: ViteDevServer) {\n if (!resolvedViewsDir) {\n const root = server.config.root || process.cwd();\n resolvedViewsDir = isAbsolute(rawViewsDir)\n ? rawViewsDir\n : resolve(root, rawViewsDir);\n projectRoot = root;\n viewEntryPattern = getViewEntryPattern(resolvedViewsDir);\n }\n\n server.watcher.add(resolvedViewsDir);\n // Track which view files we've already warned about so a rescan\n // triggered by an unrelated edit doesn't re-emit the same warning.\n let knownInvalid = new Set<string>();\n const rescan = () => {\n try {\n // Surface broken view files. Without this, files lacking a\n // default export are silently dropped from the input and the\n // user has no idea why their widget never mounts.\n const { valid, invalid } = scanViewsSync(resolvedViewsDir);\n const nextInvalid = new Set(invalid.map((v) => v.filePath));\n\n for (const filePath of nextInvalid) {\n if (!knownInvalid.has(filePath)) {\n server.config.logger.warn(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" is missing a default export — it won't be served until fixed.`,\n );\n }\n }\n for (const filePath of knownInvalid) {\n if (!nextInvalid.has(filePath)) {\n server.config.logger.info(\n `[skybridge] view file \"${relative(projectRoot, filePath)}\" resolved.`,\n );\n }\n }\n knownInvalid = nextInvalid;\n\n assertUniqueViewNames(valid);\n viewMap = new Map(valid.map((v) => [v.name, v]));\n writeViewsDts(projectRoot, valid);\n } catch (err) {\n // assertUniqueViewNames throws on duplicate view names. Catch so\n // chokidar's listener chain doesn't surface it as unhandled and\n // crash the dev server — previous viewMap stays active until\n // the user fixes the conflict.\n const message = err instanceof Error ? err.message : String(err);\n server.config.logger.error(\n `[skybridge] view rescan failed: ${message}`,\n );\n }\n };\n\n // Initial scan emits warnings for broken files that exist at startup.\n rescan();\n server.watcher.on(\"add\", rescan);\n server.watcher.on(\"change\", rescan);\n server.watcher.on(\"unlink\", rescan);\n },\n\n async transform(code, id) {\n if (viewEntryPattern?.test(id) && !hasDefaultExport(code, id)) {\n this.warn(\n `View file \"${id.split(\"/\").pop()}\" is missing a default export.`,\n );\n }\n\n return await dataLlmTransform(code, id);\n },\n };\n}\n"]}
|
package/dist/web/types.d.ts
CHANGED
|
@@ -4,13 +4,17 @@ declare module "react" {
|
|
|
4
4
|
"data-llm"?: string;
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
+
/** Shorthand for an arbitrary plain object — `Record<string, unknown>`. */
|
|
7
8
|
export type UnknownObject = Record<string, unknown>;
|
|
9
|
+
/** Flatten an intersection so it appears as a single object type in hovers and error messages. */
|
|
8
10
|
export type Prettify<T> = {
|
|
9
11
|
[K in keyof T]: T[K];
|
|
10
12
|
} & {};
|
|
13
|
+
/** Widen `T` to also satisfy {@link UnknownObject}, useful for tool input/output narrowing. */
|
|
11
14
|
export type Objectify<T> = T & UnknownObject;
|
|
12
15
|
type RequiredKeys<T> = {
|
|
13
16
|
[K in keyof T]-?: Record<string, never> extends Pick<T, K> ? never : K;
|
|
14
17
|
}[keyof T];
|
|
18
|
+
/** `true` if `T` has at least one required key, `false` if every key is optional. */
|
|
15
19
|
export type HasRequiredKeys<T> = RequiredKeys<T> extends never ? false : true;
|
|
16
20
|
export {};
|
package/dist/web/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,CAAC","sourcesContent":["import \"react\";\n\ndeclare module \"react\" {\n interface HTMLAttributes<T> {\n \"data-llm\"?: string;\n }\n}\n\nexport type UnknownObject = Record<string, unknown>;\n\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\nexport type Objectify<T> = T & UnknownObject;\n\ntype RequiredKeys<T> = {\n [K in keyof T]-?: Record<string, never> extends Pick<T, K> ? never : K;\n}[keyof T];\nexport type HasRequiredKeys<T> = RequiredKeys<T> extends never ? false : true;\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,CAAC","sourcesContent":["import \"react\";\n\ndeclare module \"react\" {\n interface HTMLAttributes<T> {\n \"data-llm\"?: string;\n }\n}\n\n/** Shorthand for an arbitrary plain object — `Record<string, unknown>`. */\nexport type UnknownObject = Record<string, unknown>;\n\n/** Flatten an intersection so it appears as a single object type in hovers and error messages. */\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n/** Widen `T` to also satisfy {@link UnknownObject}, useful for tool input/output narrowing. */\nexport type Objectify<T> = T & UnknownObject;\n\ntype RequiredKeys<T> = {\n [K in keyof T]-?: Record<string, never> extends Pick<T, K> ? never : K;\n}[keyof T];\n/** `true` if `T` has at least one required key, `false` if every key is optional. */\nexport type HasRequiredKeys<T> = RequiredKeys<T> extends never ? false : true;\n"]}
|