skybridge 0.0.0-dev.c676e5e → 0.0.0-dev.c689d72

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.
Files changed (119) hide show
  1. package/README.md +8 -8
  2. package/dist/cli/use-nodemon.js +2 -2
  3. package/dist/cli/use-nodemon.js.map +1 -1
  4. package/dist/commands/build.js +28 -7
  5. package/dist/commands/build.js.map +1 -1
  6. package/dist/commands/start.js +2 -7
  7. package/dist/commands/start.js.map +1 -1
  8. package/dist/server/content-helpers.d.ts +27 -0
  9. package/dist/server/content-helpers.js +46 -0
  10. package/dist/server/content-helpers.js.map +1 -0
  11. package/dist/server/content-helpers.test.js +70 -0
  12. package/dist/server/content-helpers.test.js.map +1 -0
  13. package/dist/server/express.js +3 -3
  14. package/dist/server/express.js.map +1 -1
  15. package/dist/server/express.test.js +39 -2
  16. package/dist/server/express.test.js.map +1 -1
  17. package/dist/server/index.d.ts +4 -3
  18. package/dist/server/index.js +3 -2
  19. package/dist/server/index.js.map +1 -1
  20. package/dist/server/inferUtilityTypes.d.ts +6 -6
  21. package/dist/server/middleware.test.js +12 -9
  22. package/dist/server/middleware.test.js.map +1 -1
  23. package/dist/server/server.d.ts +95 -72
  24. package/dist/server/server.js +191 -72
  25. package/dist/server/server.js.map +1 -1
  26. package/dist/server/templateHelper.d.ts +5 -5
  27. package/dist/server/templates/development.hbs +2 -2
  28. package/dist/server/templates/production.hbs +1 -1
  29. package/dist/server/viewsDevServer.d.ts +14 -0
  30. package/dist/server/viewsDevServer.js +45 -0
  31. package/dist/server/viewsDevServer.js.map +1 -0
  32. package/dist/test/utils.d.ts +8 -20
  33. package/dist/test/utils.js +39 -35
  34. package/dist/test/utils.js.map +1 -1
  35. package/dist/test/view.test.js +523 -0
  36. package/dist/test/view.test.js.map +1 -0
  37. package/dist/web/bridges/apps-sdk/adaptor.d.ts +2 -2
  38. package/dist/web/bridges/apps-sdk/adaptor.js +8 -2
  39. package/dist/web/bridges/apps-sdk/adaptor.js.map +1 -1
  40. package/dist/web/bridges/mcp-app/adaptor.d.ts +6 -6
  41. package/dist/web/bridges/mcp-app/adaptor.js +24 -24
  42. package/dist/web/bridges/mcp-app/adaptor.js.map +1 -1
  43. package/dist/web/bridges/types.d.ts +7 -7
  44. package/dist/web/components/modal-provider.js +1 -1
  45. package/dist/web/components/modal-provider.js.map +1 -1
  46. package/dist/web/create-store.js +9 -9
  47. package/dist/web/create-store.js.map +1 -1
  48. package/dist/web/create-store.test.js +14 -16
  49. package/dist/web/create-store.test.js.map +1 -1
  50. package/dist/web/data-llm.d.ts +1 -1
  51. package/dist/web/data-llm.js +3 -3
  52. package/dist/web/data-llm.js.map +1 -1
  53. package/dist/web/data-llm.test.js +22 -22
  54. package/dist/web/data-llm.test.js.map +1 -1
  55. package/dist/web/generate-helpers.d.ts +20 -18
  56. package/dist/web/generate-helpers.js +20 -18
  57. package/dist/web/generate-helpers.js.map +1 -1
  58. package/dist/web/generate-helpers.test-d.js +26 -26
  59. package/dist/web/generate-helpers.test-d.js.map +1 -1
  60. package/dist/web/helpers/state.d.ts +2 -2
  61. package/dist/web/helpers/state.js +11 -11
  62. package/dist/web/helpers/state.js.map +1 -1
  63. package/dist/web/helpers/state.test.js +9 -9
  64. package/dist/web/helpers/state.test.js.map +1 -1
  65. package/dist/web/hooks/index.d.ts +1 -1
  66. package/dist/web/hooks/index.js +1 -1
  67. package/dist/web/hooks/index.js.map +1 -1
  68. package/dist/web/hooks/use-request-modal.d.ts +1 -1
  69. package/dist/web/hooks/use-request-modal.js +4 -4
  70. package/dist/web/hooks/use-request-modal.js.map +1 -1
  71. package/dist/web/hooks/use-request-modal.test.js +1 -1
  72. package/dist/web/hooks/use-request-modal.test.js.map +1 -1
  73. package/dist/web/hooks/use-view-state.d.ts +4 -0
  74. package/dist/web/hooks/use-view-state.js +32 -0
  75. package/dist/web/hooks/use-view-state.js.map +1 -0
  76. package/dist/web/hooks/{use-widget-state.test.js → use-view-state.test.js} +17 -17
  77. package/dist/web/hooks/use-view-state.test.js.map +1 -0
  78. package/dist/web/index.d.ts +1 -2
  79. package/dist/web/index.js +1 -2
  80. package/dist/web/index.js.map +1 -1
  81. package/dist/web/mount-view.d.ts +1 -0
  82. package/dist/web/{mount-widget.js → mount-view.js} +2 -2
  83. package/dist/web/mount-view.js.map +1 -0
  84. package/dist/web/plugin/plugin.d.ts +4 -1
  85. package/dist/web/plugin/plugin.js +112 -25
  86. package/dist/web/plugin/plugin.js.map +1 -1
  87. package/dist/web/plugin/scan-views.d.ts +8 -0
  88. package/dist/web/plugin/scan-views.js +70 -0
  89. package/dist/web/plugin/scan-views.js.map +1 -0
  90. package/dist/web/plugin/scan-views.test.d.ts +1 -0
  91. package/dist/web/plugin/scan-views.test.js +67 -0
  92. package/dist/web/plugin/scan-views.test.js.map +1 -0
  93. package/dist/web/plugin/validate-view.d.ts +1 -0
  94. package/dist/web/plugin/validate-view.js +9 -0
  95. package/dist/web/plugin/validate-view.js.map +1 -0
  96. package/dist/web/plugin/validate-view.test.d.ts +1 -0
  97. package/dist/web/plugin/validate-view.test.js +24 -0
  98. package/dist/web/plugin/validate-view.test.js.map +1 -0
  99. package/package.json +17 -10
  100. package/tsconfig.base.json +2 -0
  101. package/dist/server/widgetsDevServer.d.ts +0 -13
  102. package/dist/server/widgetsDevServer.js +0 -52
  103. package/dist/server/widgetsDevServer.js.map +0 -1
  104. package/dist/test/widget.test.js +0 -334
  105. package/dist/test/widget.test.js.map +0 -1
  106. package/dist/web/hooks/use-widget-state.d.ts +0 -4
  107. package/dist/web/hooks/use-widget-state.js +0 -32
  108. package/dist/web/hooks/use-widget-state.js.map +0 -1
  109. package/dist/web/hooks/use-widget-state.test.js.map +0 -1
  110. package/dist/web/mount-widget.d.ts +0 -1
  111. package/dist/web/mount-widget.js.map +0 -1
  112. package/dist/web/plugin/validate-widget.d.ts +0 -5
  113. package/dist/web/plugin/validate-widget.js +0 -27
  114. package/dist/web/plugin/validate-widget.js.map +0 -1
  115. package/dist/web/plugin/validate-widget.test.js +0 -42
  116. package/dist/web/plugin/validate-widget.test.js.map +0 -1
  117. /package/dist/{test/widget.test.d.ts → server/content-helpers.test.d.ts} +0 -0
  118. /package/dist/{web/hooks/use-widget-state.test.d.ts → test/view.test.d.ts} +0 -0
  119. /package/dist/web/{plugin/validate-widget.test.d.ts → hooks/use-view-state.test.d.ts} +0 -0
@@ -0,0 +1,32 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+ import { getAdaptor, useHostContext } from "../bridges/index.js";
3
+ import { filterViewContext, injectViewContext } from "../helpers/state.js";
4
+ export function useViewState(defaultState) {
5
+ const adaptor = getAdaptor();
6
+ const viewStateFromBridge = useHostContext("viewState");
7
+ const [viewState, _setViewState] = useState(() => {
8
+ if (viewStateFromBridge !== null) {
9
+ return filterViewContext(viewStateFromBridge);
10
+ }
11
+ return typeof defaultState === "function"
12
+ ? defaultState()
13
+ : (defaultState ?? null);
14
+ });
15
+ useEffect(() => {
16
+ if (viewStateFromBridge !== null) {
17
+ _setViewState(filterViewContext(viewStateFromBridge));
18
+ }
19
+ }, [viewStateFromBridge]);
20
+ const setViewState = useCallback((state) => {
21
+ _setViewState((prevState) => {
22
+ const newState = typeof state === "function" ? state(prevState) : state;
23
+ const stateToSet = injectViewContext(newState);
24
+ if (stateToSet !== null) {
25
+ adaptor.setViewState(stateToSet);
26
+ }
27
+ return filterViewContext(stateToSet);
28
+ });
29
+ }, [adaptor]);
30
+ return [viewState, setViewState];
31
+ }
32
+ //# sourceMappingURL=use-view-state.js.map
@@ -0,0 +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;AAS3E,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"}
@@ -2,8 +2,8 @@ import { act, renderHook, waitFor } from "@testing-library/react";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
3
3
  import { McpAppAdaptor, McpAppBridge } from "../bridges/mcp-app/index.js";
4
4
  import { fireToolResultNotification, getMcpAppHostPostMessageMock, MockResizeObserver, } from "./test/utils.js";
5
- import { useWidgetState } from "./use-widget-state.js";
6
- describe("useWidgetState", () => {
5
+ import { useViewState } from "./use-view-state.js";
6
+ describe("useViewState", () => {
7
7
  let OpenaiMock;
8
8
  beforeEach(() => {
9
9
  OpenaiMock = {
@@ -21,16 +21,16 @@ describe("useWidgetState", () => {
21
21
  const windowState = { count: 5, name: "window" };
22
22
  it("should initialize with default state when window.openai.widgetState is null", () => {
23
23
  OpenaiMock.widgetState = null;
24
- const { result } = renderHook(() => useWidgetState(defaultState));
24
+ const { result } = renderHook(() => useViewState(defaultState));
25
25
  expect(result.current[0]).toEqual(defaultState);
26
26
  });
27
27
  it("should initialize with window.openai.widgetState when available", () => {
28
28
  OpenaiMock.widgetState = { modelContent: windowState };
29
- const { result } = renderHook(() => useWidgetState(defaultState));
29
+ const { result } = renderHook(() => useViewState(defaultState));
30
30
  expect(result.current[0]).toEqual(windowState);
31
31
  });
32
- it("should call window.openai.setWidgetState when setWidgetState is called with a new state", async () => {
33
- const { result } = renderHook(() => useWidgetState(defaultState));
32
+ it("should call window.openai.setWidgetState when setViewState is called with a new state", async () => {
33
+ const { result } = renderHook(() => useViewState(defaultState));
34
34
  const newState = { count: 10, name: "updated" };
35
35
  act(() => {
36
36
  result.current[1](newState);
@@ -41,8 +41,8 @@ describe("useWidgetState", () => {
41
41
  });
42
42
  expect(result.current[0]).toEqual(newState);
43
43
  });
44
- it("should call window.openai.setWidgetState when setWidgetState is called with a function updater", async () => {
45
- const { result } = renderHook(() => useWidgetState(defaultState));
44
+ it("should call window.openai.setWidgetState when setViewState is called with a function updater", async () => {
45
+ const { result } = renderHook(() => useViewState(defaultState));
46
46
  act(() => {
47
47
  result.current[1]((prev) => ({ ...prev, count: prev.count + 1 }));
48
48
  });
@@ -54,7 +54,7 @@ describe("useWidgetState", () => {
54
54
  });
55
55
  it("should update state when window.openai.widgetState changes", () => {
56
56
  OpenaiMock.widgetState = { modelContent: defaultState };
57
- const { result, rerender } = renderHook(() => useWidgetState(defaultState));
57
+ const { result, rerender } = renderHook(() => useViewState(defaultState));
58
58
  expect(result.current[0]).toEqual(defaultState);
59
59
  // Simulate window.openai.widgetState changing
60
60
  OpenaiMock.widgetState = { modelContent: windowState };
@@ -63,7 +63,7 @@ describe("useWidgetState", () => {
63
63
  expect(result.current[0]).toEqual(windowState);
64
64
  });
65
65
  });
66
- describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
66
+ describe("useViewState (mcp-app host — localStorage persistence)", () => {
67
67
  beforeEach(() => {
68
68
  vi.stubGlobal("parent", { postMessage: getMcpAppHostPostMessageMock() });
69
69
  vi.stubGlobal("skybridge", { hostType: "mcp-app" });
@@ -79,7 +79,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
79
79
  });
80
80
  it("should persist state to localStorage when viewUUID is available", async () => {
81
81
  const viewUUID = "test-uuid-123";
82
- const { result } = renderHook(() => useWidgetState({ page: 1, zoom: 100 }));
82
+ const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));
83
83
  await act(async () => {
84
84
  fireToolResultNotification({
85
85
  content: [{ type: "text", text: "result" }],
@@ -97,7 +97,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
97
97
  const viewUUID = "test-uuid-456";
98
98
  // Pre-seed with the sb:{timestamp}:{viewUUID} format
99
99
  localStorage.setItem(`sb:1700000000000:${viewUUID}`, JSON.stringify({ page: 5, zoom: 200 }));
100
- const { result } = renderHook(() => useWidgetState({ page: 1, zoom: 100 }));
100
+ const { result } = renderHook(() => useViewState({ page: 1, zoom: 100 }));
101
101
  act(() => {
102
102
  fireToolResultNotification({
103
103
  content: [{ type: "text", text: "result" }],
@@ -110,7 +110,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
110
110
  });
111
111
  });
112
112
  it("should not persist when no viewUUID is available", () => {
113
- const { result } = renderHook(() => useWidgetState({ page: 1 }));
113
+ const { result } = renderHook(() => useViewState({ page: 1 }));
114
114
  act(() => {
115
115
  result.current[1]({ page: 2 });
116
116
  });
@@ -120,7 +120,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
120
120
  it("should handle corrupted localStorage data gracefully", async () => {
121
121
  const viewUUID = "test-uuid-corrupt";
122
122
  localStorage.setItem(`sb:1700000000000:${viewUUID}`, "not valid json{{{");
123
- const { result } = renderHook(() => useWidgetState({ page: 1 }));
123
+ const { result } = renderHook(() => useViewState({ page: 1 }));
124
124
  act(() => {
125
125
  fireToolResultNotification({
126
126
  content: [{ type: "text", text: "result" }],
@@ -136,7 +136,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
136
136
  const viewUUID = "lru-test-uuid";
137
137
  const oldKey = `sb:1000000000000:${viewUUID}`;
138
138
  localStorage.setItem(oldKey, JSON.stringify({ page: 1 }));
139
- const { result } = renderHook(() => useWidgetState({ page: 1 }));
139
+ const { result } = renderHook(() => useViewState({ page: 1 }));
140
140
  await act(async () => {
141
141
  fireToolResultNotification({
142
142
  content: [{ type: "text", text: "result" }],
@@ -158,7 +158,7 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
158
158
  }
159
159
  expect(localStorage.length).toBe(200);
160
160
  const viewUUID = "eviction-test-uuid";
161
- const { result } = renderHook(() => useWidgetState({ page: 1 }));
161
+ const { result } = renderHook(() => useViewState({ page: 1 }));
162
162
  await act(async () => {
163
163
  fireToolResultNotification({
164
164
  content: [{ type: "text", text: "result" }],
@@ -174,4 +174,4 @@ describe("useWidgetState (mcp-app host — localStorage persistence)", () => {
174
174
  expect(localStorage.getItem("sb:1000000000000:old-uuid-0000")).toBeNull();
175
175
  });
176
176
  });
177
- //# sourceMappingURL=use-widget-state.test.js.map
177
+ //# sourceMappingURL=use-view-state.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-view-state.test.js","sourceRoot":"","sources":["../../../src/web/hooks/use-view-state.test.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,5 @@ export { createStore } from "./create-store.js";
3
3
  export * from "./data-llm.js";
4
4
  export { generateHelpers } from "./generate-helpers.js";
5
5
  export * from "./hooks/index.js";
6
- export { mountWidget } from "./mount-widget.js";
7
- export { skybridge } from "./plugin/plugin.js";
6
+ export { mountView } from "./mount-view.js";
8
7
  export * from "./types.js";
package/dist/web/index.js CHANGED
@@ -3,7 +3,6 @@ export { createStore } from "./create-store.js";
3
3
  export * from "./data-llm.js";
4
4
  export { generateHelpers } from "./generate-helpers.js";
5
5
  export * from "./hooks/index.js";
6
- export { mountWidget } from "./mount-widget.js";
7
- export { skybridge } from "./plugin/plugin.js";
6
+ export { mountView } from "./mount-view.js";
8
7
  export * from "./types.js";
9
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/web/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,YAAY,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const mountView: (component: React.ReactNode) => void;
@@ -3,7 +3,7 @@ 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
- export const mountWidget = (component) => {
6
+ export const mountView = (component) => {
7
7
  const rootElement = document.getElementById("root");
8
8
  if (!rootElement) {
9
9
  throw new Error("Root element not found");
@@ -24,4 +24,4 @@ export const mountWidget = (component) => {
24
24
  rootInstance.render(createElement(StrictMode, null, app));
25
25
  })();
26
26
  };
27
- //# sourceMappingURL=mount-widget.js.map
27
+ //# sourceMappingURL=mount-view.js.map
@@ -0,0 +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"}
@@ -1,2 +1,5 @@
1
1
  import type { Plugin } from "vite";
2
- export declare function skybridge(): Plugin;
2
+ export interface SkybridgePluginOptions {
3
+ viewsDir?: string;
4
+ }
5
+ export declare function skybridge(options?: SkybridgePluginOptions): Plugin;
@@ -1,29 +1,51 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { discoverViewsSync, writeViewsDts, } from "./scan-views.js";
1
3
  import { transform as dataLlmTransform } from "./transform-data-llm.js";
2
- import { validateWidget } from "./validate-widget.js";
3
- // Matches widget entry files (e.g. src/widgets/foo.tsx, src/widgets/foo/index.tsx) with optional Vite query strings
4
- const WIDGET_ENTRY_RE = /\/src\/widgets\/(?:[^/]+\.(?:jsx|tsx)|[^/]+\/index\.tsx)(?:\?.*)?$/;
5
- export function skybridge() {
4
+ import { hasDefaultExport } from "./validate-view.js";
5
+ const VIRTUAL_PREFIX = "/_skybridge/view/";
6
+ const VIRTUAL_MODULE_PREFIX = "\0skybridge:view:";
7
+ function buildVirtualEntry(viewFilePath) {
8
+ const normalized = viewFilePath.replace(/\\/g, "/");
9
+ return [
10
+ `import { mountView } from "skybridge/web";`,
11
+ `import Component from "${normalized}";`,
12
+ `import { createElement } from "react";`,
13
+ `mountView(createElement(Component));`,
14
+ ].join("\n");
15
+ }
16
+ function getViewEntryPattern(viewsDir) {
17
+ const escaped = viewsDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18
+ return new RegExp(`${escaped}\\/(?:[^/]+\\.(?:jsx|tsx)|[^/]+\\/index\\.(?:tsx|jsx))(?:\\?.*)?$`);
19
+ }
20
+ export function skybridge(options) {
21
+ const rawViewsDir = options?.viewsDir ?? "src/views";
22
+ let resolvedViewsDir;
23
+ let projectRoot;
24
+ let viewMap = new Map();
25
+ let viewEntryPattern;
6
26
  return {
7
27
  name: "skybridge",
8
- async config(config) {
9
- // Dynamic imports to ensure Node modules are only loaded in Node.js context
10
- const { globSync } = await import("node:fs");
11
- const { basename, dirname, parse, resolve } = await import("node:path");
12
- const projectRoot = config.root || process.cwd();
13
- const flatWidgetPattern = resolve(projectRoot, "src/widgets/*.{js,ts,jsx,tsx,html}");
14
- const dirWidgetPattern = resolve(projectRoot, "src/widgets/*/index.tsx");
15
- const flatWidgets = globSync(flatWidgetPattern).map((file) => {
16
- const name = parse(file).name;
17
- return [name, file];
18
- });
19
- const dirWidgets = globSync(dirWidgetPattern).map((file) => {
20
- const name = basename(dirname(file));
21
- return [name, file];
22
- });
23
- const input = Object.fromEntries([...flatWidgets, ...dirWidgets]);
28
+ enforce: "pre",
29
+ // Read by `skybridge build` to resolve viewsDir before `tsc -b` runs.
30
+ api: { viewsDir: rawViewsDir },
31
+ config(config) {
32
+ projectRoot = config.root || process.cwd();
33
+ resolvedViewsDir = isAbsolute(rawViewsDir)
34
+ ? rawViewsDir
35
+ : resolve(projectRoot, rawViewsDir);
36
+ viewEntryPattern = getViewEntryPattern(resolvedViewsDir);
37
+ const views = discoverViewsSync(resolvedViewsDir);
38
+ viewMap = new Map(views.map((v) => [v.name, v]));
39
+ writeViewsDts(projectRoot, views);
40
+ const input = {};
41
+ for (const view of views) {
42
+ input[view.name] = `${VIRTUAL_PREFIX}${view.name}`;
43
+ }
24
44
  return {
25
45
  base: "/assets",
26
46
  build: {
47
+ outDir: "dist/assets",
48
+ emptyOutDir: true,
27
49
  manifest: true,
28
50
  minify: true,
29
51
  cssCodeSplit: false,
@@ -31,6 +53,27 @@ export function skybridge() {
31
53
  input,
32
54
  },
33
55
  },
56
+ // Pre-bundle view deps at startup so the first tool invocation
57
+ // doesn't hit Vite's on-demand re-optimization path (which sends
58
+ // `full-reload` over HMR — in our iframe flow the parent host
59
+ // can't honour a reload, and the view silently never mounts).
60
+ optimizeDeps: {
61
+ // Scan view files so transitive user deps (zod, tailwind, etc.)
62
+ // get pre-bundled at startup.
63
+ entries: [
64
+ `${resolvedViewsDir}/*.{tsx,jsx}`,
65
+ `${resolvedViewsDir}/*/index.{tsx,jsx}`,
66
+ ],
67
+ // Framework deps imported by the synthesized virtual entry.
68
+ // The scanner can't see the virtual module source, so we must
69
+ // list these explicitly.
70
+ include: [
71
+ "react",
72
+ "react-dom/client",
73
+ "react/jsx-runtime",
74
+ "skybridge/web",
75
+ ],
76
+ },
34
77
  experimental: {
35
78
  renderBuiltUrl: (filename) => {
36
79
  return {
@@ -40,12 +83,56 @@ export function skybridge() {
40
83
  },
41
84
  };
42
85
  },
43
- enforce: "pre",
44
- async transform(code, id) {
45
- if (WIDGET_ENTRY_RE.test(id)) {
46
- for (const warning of validateWidget(code, id)) {
47
- this.warn(warning.message);
86
+ resolveId(id) {
87
+ if (id.startsWith(VIRTUAL_PREFIX)) {
88
+ const name = id.slice(VIRTUAL_PREFIX.length);
89
+ if (viewMap.has(name)) {
90
+ return `${VIRTUAL_MODULE_PREFIX}${name}`;
91
+ }
92
+ }
93
+ return null;
94
+ },
95
+ load(id) {
96
+ if (id.startsWith(VIRTUAL_MODULE_PREFIX)) {
97
+ const name = id.slice(VIRTUAL_MODULE_PREFIX.length);
98
+ const view = viewMap.get(name);
99
+ if (view) {
100
+ return buildVirtualEntry(view.filePath);
101
+ }
102
+ }
103
+ return null;
104
+ },
105
+ configureServer(server) {
106
+ if (!resolvedViewsDir) {
107
+ const root = server.config.root || process.cwd();
108
+ resolvedViewsDir = isAbsolute(rawViewsDir)
109
+ ? rawViewsDir
110
+ : resolve(root, rawViewsDir);
111
+ projectRoot = root;
112
+ viewEntryPattern = getViewEntryPattern(resolvedViewsDir);
113
+ }
114
+ server.watcher.add(resolvedViewsDir);
115
+ const rescan = () => {
116
+ try {
117
+ const views = discoverViewsSync(resolvedViewsDir);
118
+ viewMap = new Map(views.map((v) => [v.name, v]));
119
+ writeViewsDts(projectRoot, views);
48
120
  }
121
+ catch (err) {
122
+ // discoverViewsSync throws on duplicate view names. Catch so
123
+ // chokidar's listener chain doesn't surface it as unhandled and
124
+ // crash the dev server — previous viewMap stays active until
125
+ // the user fixes the conflict.
126
+ const message = err instanceof Error ? err.message : String(err);
127
+ server.config.logger.error(`[skybridge] view rescan failed: ${message}`);
128
+ }
129
+ };
130
+ server.watcher.on("add", rescan);
131
+ server.watcher.on("unlink", rescan);
132
+ },
133
+ async transform(code, id) {
134
+ if (viewEntryPattern?.test(id) && !hasDefaultExport(code, id)) {
135
+ this.warn(`View file "${id.split("/").pop()}" is missing a default export.`);
49
136
  }
50
137
  return await dataLlmTransform(code, id);
51
138
  },
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,IAAI,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,oHAAoH;AACpH,MAAM,eAAe,GACnB,oEAAoE,CAAC;AAEvE,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,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YAExE,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YACjD,MAAM,iBAAiB,GAAG,OAAO,CAC/B,WAAW,EACX,oCAAoC,CACrC,CAAC;YACF,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC;YAEzE,MAAM,WAAW,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC3D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;gBAC9B,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACzD,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;gBACrC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACtB,CAAC,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,WAAW,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC;YAElE,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;gBACD,YAAY,EAAE;oBACZ,cAAc,EAAE,CAAC,QAAQ,EAAE,EAAE;wBAC3B,OAAO;4BACL,OAAO,EAAE,yCAAyC,QAAQ,GAAG;yBAC9D,CAAC;oBACJ,CAAC;iBACF;aACF,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,KAAK;QACd,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE;YACtB,IAAI,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC7B,KAAK,MAAM,OAAO,IAAI,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;oBAC/C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;YAED,OAAO,MAAM,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/web/plugin/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface DiscoveredView {
2
+ name: string;
3
+ filePath: string;
4
+ }
5
+ export declare function discoverViewsSync(viewsDir: string): DiscoveredView[];
6
+ export declare function generateViewsDts(views: DiscoveredView[]): string;
7
+ export declare function writeViewsDts(projectRoot: string, views: DiscoveredView[]): void;
8
+ export declare function scanAndWriteViewsDts(projectRoot?: string, viewsDir?: string): void;
@@ -0,0 +1,70 @@
1
+ import { globSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, dirname, isAbsolute, join, parse, resolve } from "node:path";
3
+ import { hasDefaultExport } from "./validate-view.js";
4
+ export function discoverViewsSync(viewsDir) {
5
+ const flatPattern = resolve(viewsDir, "*.{tsx,jsx}");
6
+ const dirPattern = resolve(viewsDir, "*/index.{tsx,jsx}");
7
+ const flatFiles = globSync(flatPattern).map((file) => ({
8
+ name: parse(file).name,
9
+ filePath: file,
10
+ }));
11
+ const dirFiles = globSync(dirPattern).map((file) => ({
12
+ name: basename(dirname(file)),
13
+ filePath: file,
14
+ }));
15
+ // Filter first, then check duplicates — so a barrel file like
16
+ // `views/foo/index.tsx` (no default export) doesn't falsely collide with
17
+ // a sibling view at `views/foo.tsx`.
18
+ const views = [...flatFiles, ...dirFiles]
19
+ .filter((v) => v.name !== "index")
20
+ .filter((v) => hasDefaultExport(readFileSync(v.filePath, "utf-8"), v.filePath));
21
+ const nameMap = new Map();
22
+ for (const view of views) {
23
+ const paths = nameMap.get(view.name) ?? [];
24
+ paths.push(view.filePath);
25
+ nameMap.set(view.name, paths);
26
+ }
27
+ for (const [name, paths] of nameMap) {
28
+ if (paths.length > 1) {
29
+ throw new Error(`skybridge: duplicate view name "${name}" resolved from:\n - ${paths.join("\n - ")}\nRename one of the files to avoid the conflict.`);
30
+ }
31
+ }
32
+ return views;
33
+ }
34
+ export function generateViewsDts(views) {
35
+ const entries = views.map((v) => ` "${v.name}": true;`).join("\n");
36
+ return [
37
+ "export {};",
38
+ "",
39
+ 'declare module "skybridge/server" {',
40
+ " interface ViewNameRegistry {",
41
+ entries,
42
+ " }",
43
+ "}",
44
+ "",
45
+ ].join("\n");
46
+ }
47
+ export function writeViewsDts(projectRoot, views) {
48
+ const dir = join(projectRoot, ".skybridge");
49
+ mkdirSync(dir, { recursive: true });
50
+ const filePath = join(dir, "views.d.ts");
51
+ const content = generateViewsDts(views);
52
+ try {
53
+ const existing = readFileSync(filePath, "utf-8");
54
+ if (existing === content) {
55
+ return;
56
+ }
57
+ }
58
+ catch {
59
+ // File doesn't exist yet
60
+ }
61
+ writeFileSync(filePath, content, "utf-8");
62
+ }
63
+ export function scanAndWriteViewsDts(projectRoot, viewsDir) {
64
+ const root = projectRoot ?? process.cwd();
65
+ const rawDir = viewsDir ?? "src/views";
66
+ const resolvedDir = isAbsolute(rawDir) ? rawDir : resolve(root, rawDir);
67
+ const views = discoverViewsSync(resolvedDir);
68
+ writeViewsDts(root, views);
69
+ }
70
+ //# sourceMappingURL=scan-views.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-views.js","sourceRoot":"","sources":["../../../src/web/plugin/scan-views.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,67 @@
1
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { discoverViewsSync, scanAndWriteViewsDts, writeViewsDts, } from "./scan-views.js";
6
+ const DEFAULT_EXPORT = "export default function V() { return null; }";
7
+ describe("discoverViewsSync", () => {
8
+ let root;
9
+ let viewsDir;
10
+ beforeEach(() => {
11
+ root = mkdtempSync(join(tmpdir(), "skybridge-scan-"));
12
+ viewsDir = join(root, "views");
13
+ mkdirSync(viewsDir, { recursive: true });
14
+ });
15
+ afterEach(() => {
16
+ rmSync(root, { recursive: true, force: true });
17
+ });
18
+ it("picks up flat and dir-index views", () => {
19
+ writeFileSync(join(viewsDir, "a.tsx"), DEFAULT_EXPORT);
20
+ mkdirSync(join(viewsDir, "my-view"));
21
+ writeFileSync(join(viewsDir, "my-view/index.tsx"), DEFAULT_EXPORT);
22
+ expect(discoverViewsSync(viewsDir)
23
+ .map((v) => v.name)
24
+ .sort()).toEqual(["a", "my-view"]);
25
+ });
26
+ it("throws on duplicate view names (flat + dir-index collision)", () => {
27
+ writeFileSync(join(viewsDir, "dup.tsx"), DEFAULT_EXPORT);
28
+ mkdirSync(join(viewsDir, "dup"));
29
+ writeFileSync(join(viewsDir, "dup/index.tsx"), DEFAULT_EXPORT);
30
+ expect(() => discoverViewsSync(viewsDir)).toThrow(/duplicate view name "dup"/);
31
+ });
32
+ });
33
+ describe("writeViewsDts", () => {
34
+ let root;
35
+ beforeEach(() => {
36
+ root = mkdtempSync(join(tmpdir(), "skybridge-dts-"));
37
+ });
38
+ afterEach(() => {
39
+ rmSync(root, { recursive: true, force: true });
40
+ });
41
+ it("is a no-op when content is unchanged", () => {
42
+ const views = [{ name: "a", filePath: "/a.tsx" }];
43
+ writeViewsDts(root, views);
44
+ const dtsPath = join(root, ".skybridge", "views.d.ts");
45
+ const firstMtime = statSync(dtsPath).mtimeMs;
46
+ writeViewsDts(root, views);
47
+ expect(statSync(dtsPath).mtimeMs).toBe(firstMtime);
48
+ });
49
+ });
50
+ describe("scanAndWriteViewsDts", () => {
51
+ let root;
52
+ beforeEach(() => {
53
+ root = mkdtempSync(join(tmpdir(), "skybridge-scan-dts-"));
54
+ mkdirSync(join(root, "src/views"), { recursive: true });
55
+ writeFileSync(join(root, "src/views/hello.tsx"), DEFAULT_EXPORT);
56
+ });
57
+ afterEach(() => {
58
+ rmSync(root, { recursive: true, force: true });
59
+ });
60
+ it("writes a views.d.ts that augments skybridge/server with discovered view names", () => {
61
+ scanAndWriteViewsDts(root);
62
+ const content = readFileSync(join(root, ".skybridge/views.d.ts"), "utf-8");
63
+ expect(content).toContain('declare module "skybridge/server"');
64
+ expect(content).toContain('"hello": true;');
65
+ });
66
+ });
67
+ //# sourceMappingURL=scan-views.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-views.test.js","sourceRoot":"","sources":["../../../src/web/plugin/scan-views.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function hasDefaultExport(code: string, _filePath?: string): boolean;
@@ -0,0 +1,9 @@
1
+ function stripComments(code) {
2
+ return code.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
3
+ }
4
+ export function hasDefaultExport(code, _filePath) {
5
+ const stripped = stripComments(code);
6
+ return (/export\s+default\s/.test(stripped) ||
7
+ /export\s*\{[^}]*\bas\s+default\b[^}]*}/.test(stripped));
8
+ }
9
+ //# sourceMappingURL=validate-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-view.js","sourceRoot":"","sources":["../../../src/web/plugin/validate-view.ts"],"names":[],"mappings":"AAAA,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,SAAkB;IAC/D,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,CACL,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC;QACnC,wCAAwC,CAAC,IAAI,CAAC,QAAQ,CAAC,CACxD,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};