skybridge 0.0.0-dev.d4358f5 → 0.0.0-dev.d455164
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 +37 -31
- package/dist/cli/build-helpers.d.ts +11 -0
- package/dist/cli/build-helpers.js +107 -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 +132 -0
- package/dist/cli/build-helpers.test.js.map +1 -0
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.d.ts +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.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/header.d.ts +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/resolve-skybridge-config.js +18 -0
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +89 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +14 -0
- package/dist/cli/use-nodemon.js +71 -60
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-typescript-check.d.ts +8 -2
- package/dist/cli/use-typescript-check.js +71 -68
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -3
- package/dist/commands/build.js +2 -73
- 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.d.ts +1 -0
- package/dist/commands/dev.js +48 -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/context-warnings.d.ts +5 -0
- package/dist/context-warnings.js +31 -0
- package/dist/context-warnings.js.map +1 -0
- package/dist/context-warnings.test.d.ts +1 -0
- package/dist/context-warnings.test.js +28 -0
- package/dist/context-warnings.test.js.map +1 -0
- 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 +32 -0
- package/dist/server/auth/discovery.js +56 -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 +44 -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 +40 -0
- package/dist/server/auth/providers/auth0.js +31 -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 +50 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/authplane.d.ts +49 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.d.ts +1 -0
- package/dist/server/auth/providers/authplane.test.js +140 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +51 -0
- package/dist/server/auth/providers/clerk.js +19 -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 +30 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +40 -0
- package/dist/server/auth/providers/custom.js +56 -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 +158 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +44 -0
- package/dist/server/auth/providers/descope.js +44 -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 +62 -0
- package/dist/server/auth/providers/descope.test.js.map +1 -0
- package/dist/server/auth/providers/shared.d.ts +2 -0
- package/dist/server/auth/providers/shared.js +6 -0
- package/dist/server/auth/providers/shared.js.map +1 -0
- package/dist/server/auth/providers/shared.test.d.ts +1 -0
- package/dist/server/auth/providers/shared.test.js +10 -0
- package/dist/server/auth/providers/shared.test.js.map +1 -0
- package/dist/server/auth/providers/stytch.d.ts +25 -0
- package/dist/server/auth/providers/stytch.js +16 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/verify-spy.d.ts +12 -0
- package/dist/server/auth/providers/verify-spy.js +21 -0
- package/dist/server/auth/providers/verify-spy.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +33 -0
- package/dist/server/auth/providers/workos.js +15 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +6 -0
- package/dist/server/auth/setup.js +92 -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 +508 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +46 -0
- package/dist/server/auth/verify.js +62 -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 +149 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth-extra.test-d.d.ts +1 -0
- package/dist/server/auth-extra.test-d.js +82 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +58 -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/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +15 -2
- package/dist/server/index.js +13 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.d.ts +47 -9
- package/dist/server/middleware.js +33 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/middleware.test.js +37 -1
- package/dist/server/middleware.test.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 +288 -19
- package/dist/server/server.js +449 -175
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +79 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +29 -0
- package/dist/server/skills.js +195 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +247 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/view-name.test-d.d.ts +1 -0
- package/dist/server/view-name.test-d.js +8 -0
- package/dist/server/view-name.test-d.js.map +1 -0
- package/dist/server/view-resource-resolution.test.d.ts +6 -0
- package/dist/server/view-resource-resolution.test.js +171 -0
- package/dist/server/view-resource-resolution.test.js.map +1 -0
- package/dist/server/viewsDevServer.js +4 -1
- package/dist/server/viewsDevServer.js.map +1 -1
- package/dist/test/utils.d.ts +3 -3
- package/dist/test/view.test.js +125 -96
- package/dist/test/view.test.js.map +1 -1
- package/dist/web/bridges/adaptor.d.ts +51 -0
- package/dist/web/bridges/adaptor.js +327 -0
- package/dist/web/bridges/adaptor.js.map +1 -0
- package/dist/web/bridges/adaptor.test.d.ts +1 -0
- package/dist/web/bridges/adaptor.test.js +225 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +6 -1
- package/dist/web/bridges/apps-sdk/bridge.js +15 -3
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/index.d.ts +0 -1
- package/dist/web/bridges/apps-sdk/index.js +0 -1
- package/dist/web/bridges/apps-sdk/index.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 +9 -7
- package/dist/web/bridges/get-adaptor.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.d.ts +1 -0
- package/dist/web/bridges/get-adaptor.test.js +32 -0
- package/dist/web/bridges/get-adaptor.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/bridge.d.ts +15 -2
- package/dist/web/bridges/mcp-app/bridge.js +73 -7
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.d.ts +1 -0
- package/dist/web/bridges/mcp-app/bridge.test.js +17 -0
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -0
- package/dist/web/bridges/mcp-app/index.d.ts +0 -1
- package/dist/web/bridges/mcp-app/index.js +0 -1
- package/dist/web/bridges/mcp-app/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +2 -0
- package/dist/web/bridges/mcp-app/types.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 +143 -0
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -0
- package/dist/web/bridges/types.d.ts +104 -1
- package/dist/web/bridges/types.js +14 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/bridges/types.test.d.ts +1 -0
- package/dist/web/bridges/types.test.js +19 -0
- package/dist/web/bridges/types.test.js.map +1 -0
- 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/components/modal-provider.d.ts +1 -1
- package/dist/web/components/modal-provider.js +4 -3
- package/dist/web/components/modal-provider.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/create-store.test.js +8 -5
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +34 -1
- package/dist/web/data-llm.js +28 -0
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +18 -5
- package/dist/web/data-llm.test.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 +5 -0
- package/dist/web/hooks/index.js +5 -0
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +7 -2
- package/dist/web/hooks/test/utils.js +14 -3
- 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 +62 -23
- 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-display-mode.test.js +56 -20
- package/dist/web/hooks/use-display-mode.test.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 +103 -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-files.test.js +10 -2
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host-info.d.ts +22 -0
- package/dist/web/hooks/use-host-info.js +30 -0
- package/dist/web/hooks/use-host-info.js.map +1 -0
- package/dist/web/hooks/use-host-info.test.d.ts +1 -0
- package/dist/web/hooks/use-host-info.test.js +59 -0
- package/dist/web/hooks/use-host-info.test.js.map +1 -0
- 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-layout.test.js +59 -26
- package/dist/web/hooks/use-layout.test.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-open-external.test.js +13 -3
- package/dist/web/hooks/use-open-external.test.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 +47 -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-modal.test.js +10 -0
- package/dist/web/hooks/use-request-modal.test.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 +47 -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-set-open-in-app-url.test.js +28 -8
- package/dist/web/hooks/use-set-open-in-app-url.test.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 +42 -32
- 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-user.test.js +81 -35
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +21 -0
- package/dist/web/hooks/use-view-state.js +19 -9
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +37 -2
- package/dist/web/hooks/use-view-state.test.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-build.test.d.ts +1 -0
- package/dist/web/plugin/plugin-build.test.js +54 -0
- package/dist/web/plugin/plugin-build.test.js.map +1 -0
- package/dist/web/plugin/plugin.d.ts +34 -0
- package/dist/web/plugin/plugin.js +51 -7
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/plugin.test.d.ts +1 -0
- package/dist/web/plugin/plugin.test.js +61 -0
- package/dist/web/plugin/plugin.test.js.map +1 -0
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +27 -19
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -24
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -96
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -48
- package/dist/web/bridges/mcp-app/adaptor.js +0 -263
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://docs.skybridge.tech">
|
|
5
|
-
<
|
|
5
|
+
<picture>
|
|
6
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/skybridge-readme-banner-dark.png" />
|
|
7
|
+
<img alt="Skybridge, the full-stack React framework for MCP apps and MCP servers" src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/skybridge-readme-banner-light.png" width="100%" />
|
|
8
|
+
</picture>
|
|
6
9
|
</a>
|
|
7
10
|
</p>
|
|
8
11
|
|
|
@@ -25,85 +28,86 @@
|
|
|
25
28
|
|
|
26
29
|
## About Skybridge
|
|
27
30
|
|
|
28
|
-
Skybridge helps developers build type-safe MCP
|
|
31
|
+
Skybridge helps developers build type-safe MCP apps for Claude, ChatGPT and other UI-enabled MCP clients, with a complete set of tooling designed for both humans and agents.
|
|
29
32
|
|
|
30
|
-
MCP
|
|
33
|
+
Why? MCP apps extend the [Model Context Protocol](https://modelcontextprotocol.io/docs/getting-started/intro) with **rich, interactive UI views** rendered from MCP servers. Conversational apps need seamless interaction between the user, the UI, and the model. This means new UX patterns, developer tooling, and abstractions.
|
|
34
|
+
Plus, the raw SDKs are low-level: no hooks, type safety, HMR, etc.
|
|
35
|
+
|
|
36
|
+
That's why we built *Skybridge*.
|
|
31
37
|
|
|
32
38
|
Features include:
|
|
33
39
|
|
|
34
|
-
- **Delightful dev environment**:
|
|
35
|
-
- **Write once, run everywhere**:
|
|
36
|
-
- **Agent-ready**:
|
|
37
|
-
- **Type-safe end-to-end**: tRPC-style inference from MCP server tool definition to React view for type
|
|
40
|
+
- **Delightful dev environment**: Skybridge provides a dev server with a local emulator, hot module reload, and a permanent tunnel to connect your local app to Claude and ChatGPT.
|
|
41
|
+
- **Write once, run everywhere**: the framework abstracts implementation differences between MCP clients, so your app runs seamlessly in Claude, ChatGPT, VSCode, and any other MCP apps compatible client.
|
|
42
|
+
- **Agent-ready**: powerful skills, CLI, and programmatic dev tool APIs, everything your coding agent needs to build MCP apps end-to-end.
|
|
43
|
+
- **Type-safe end-to-end**: tRPC-style inference from MCP server tool definition to React view for type safety from server to frontend.
|
|
38
44
|
- **React-first**: Intuitive React Query-style hooks, with advanced state management.
|
|
39
|
-
- **
|
|
45
|
+
- **Example library**: get started quickly with ChatGPT- and Claude-ready app examples for ecommerce, travel, SaaS, and more.
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
They chose to build their MCP apps with Skybridge:
|
|
42
48
|
|
|
43
49
|
<p align="center">
|
|
44
|
-
<a href="https://www.datadoghq.com"><
|
|
50
|
+
<a href="https://www.datadoghq.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/datadog-dark.svg"><img src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/datadog-light.svg" alt="Datadog" height="24"></picture></a>
|
|
45
51
|
|
|
46
|
-
<a href="https://bitmovin.com"><
|
|
52
|
+
<a href="https://bitmovin.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/bitmovin-dark.svg"><img src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/bitmovin-light.svg" alt="Bitmovin" height="22"></picture></a>
|
|
47
53
|
|
|
48
|
-
<a href="https://www.evaneos.com"><
|
|
54
|
+
<a href="https://www.evaneos.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/evaneos-dark.svg"><img src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/evaneos-light.svg" alt="Evaneos" height="18"></picture></a>
|
|
49
55
|
|
|
50
|
-
<a href="https://www.touchstream.media"><
|
|
56
|
+
<a href="https://www.touchstream.media"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/touchstream-dark.svg"><img src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/touchstream-light.svg" alt="Touchstream" height="24"></picture></a>
|
|
51
57
|
|
|
52
|
-
<a href="https://www.cottages.com"><
|
|
58
|
+
<a href="https://www.cottages.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/cottages-dark.svg"><img src="https://raw.githubusercontent.com/alpic-ai/skybridge/main/docs/images/user-logos/cottages-light.svg" alt="Cottages.com" height="24"></picture></a>
|
|
53
59
|
</p>
|
|
54
60
|
|
|
55
61
|
## Get started
|
|
56
62
|
|
|
57
63
|
**For agents**
|
|
58
64
|
|
|
59
|
-
Install our [
|
|
65
|
+
Install our [skill](https://docs.skybridge.tech/devtools/skills) for building MCP apps and ChatGPT apps:
|
|
60
66
|
```bash
|
|
61
67
|
npx skills add alpic-ai/skybridge -s skybridge
|
|
62
68
|
```
|
|
63
|
-
Once installed,
|
|
69
|
+
Once installed, ask your agent "What skills do you have?" to confirm, then try:
|
|
64
70
|
|
|
65
|
-
- _Create a new MCP
|
|
66
|
-
- _Migrate my MCP
|
|
67
|
-
- _Add a new view to my MCP
|
|
71
|
+
- _Create a new MCP app_
|
|
72
|
+
- _Migrate my MCP server to the Skybridge framework_
|
|
73
|
+
- _Add a new view to my MCP app_
|
|
68
74
|
|
|
69
75
|
**For humans**
|
|
70
76
|
|
|
71
77
|
Bootstrap a new project with:
|
|
72
78
|
```bash
|
|
73
|
-
npm create skybridge my-app
|
|
79
|
+
npm create skybridge@latest my-app
|
|
74
80
|
```
|
|
75
|
-
For full install instructions, read
|
|
81
|
+
For full install instructions, read our [**Quickstart guide**](https://docs.skybridge.tech/quickstart/create-new-app).
|
|
76
82
|
|
|
77
83
|
## Documentation
|
|
78
84
|
|
|
79
85
|
The [Skybridge documentation](https://docs.skybridge.tech) covers the full lifecycle of building MCP Apps:
|
|
80
86
|
|
|
81
87
|
- [Fundamentals](https://docs.skybridge.tech/fundamentals): understand MCP Apps, ChatGPT Apps, and how Skybridge bridges both runtimes.
|
|
82
|
-
- [Core concepts](https://docs.skybridge.tech/concepts): learn about server <> model <> UI data
|
|
88
|
+
- [Core concepts](https://docs.skybridge.tech/concepts): learn about server <> model <> UI data flows, LLM context sync, type safety, and instant local iteration with our devtools.
|
|
83
89
|
- [Guides](https://docs.skybridge.tech/guides/fetching-data): build real app behavior with tools, views, state, and model communication.
|
|
84
90
|
- [API Reference](https://docs.skybridge.tech/api-reference): browse our MCP server APIs, React hooks, CLI commands, and runtime compatibility.
|
|
85
91
|
|
|
86
92
|
## Deploy
|
|
87
93
|
|
|
88
|
-
Deploy Skybridge apps instantly on [Alpic](https://alpic.ai)
|
|
94
|
+
Deploy Skybridge apps instantly on [Alpic](https://alpic.ai) for scalable hosting, MCP-specific analytics, permanent tunneling, app store compliance auditing and submission help. You can also self-host on any Node.js-compatible platform.
|
|
89
95
|
|
|
90
|
-
|
|
96
|
+
See our [deployment guide](https://docs.skybridge.tech/quickstart/deploy) for the full production path.
|
|
91
97
|
|
|
92
98
|
## Community & Contributing
|
|
93
99
|
|
|
94
|
-
We
|
|
95
|
-
|
|
96
|
-
Here are a few ways you can get involved:
|
|
100
|
+
We'd love your help improving Skybridge. Here are a few ways to get involved:
|
|
97
101
|
|
|
98
|
-
- **
|
|
99
|
-
- **Questions and
|
|
100
|
-
- **Pull requests**: For code or documentation changes,
|
|
102
|
+
- **Bugs**: If you run into a bug or unexpected behavior, open a [GitHub Issue](https://github.com/alpic-ai/skybridge/issues) with a clear reproduction.
|
|
103
|
+
- **Questions and ideas**: Need help building with Skybridge or have ideas to improve the framework, docs, examples, or developer experience? [Open an issue](https://github.com/alpic-ai/skybridge/issues) or share them on our [Discord](https://discord.com/invite/gNAazGueab).
|
|
104
|
+
- **Pull requests**: For code or documentation changes, read the [Contributing Guide](https://github.com/alpic-ai/skybridge/blob/main/CONTRIBUTING.md) before opening a PR.
|
|
101
105
|
|
|
102
106
|
Skybridge is released under the [MIT License](https://github.com/alpic-ai/skybridge/blob/main/LICENSE).
|
|
103
107
|
|
|
104
108
|
### Contributors
|
|
105
109
|
|
|
106
|
-
Built and maintained by [Harijoe](https://github.com/harijoe), [Fred Barthelet](https://github.com/fredericbarthelet), and the [Alpic](https://alpic.ai) team.
|
|
110
|
+
Built and maintained with ❤️ by [Harijoe](https://github.com/harijoe), [Fred Barthelet](https://github.com/fredericbarthelet), and the [Alpic](https://alpic.ai) team.
|
|
107
111
|
|
|
108
112
|
<a href="https://github.com/alpic-ai/skybridge/graphs/contributors">
|
|
109
113
|
<img src="https://contrib.rocks/image?repo=alpic-ai/skybridge" alt="Skybridge contributors">
|
|
@@ -135,10 +139,12 @@ Explore all our example templates in the [Examples](https://docs.skybridge.tech/
|
|
|
135
139
|
|
|
136
140
|
| Preview | Provider | Description | Code |
|
|
137
141
|
| --- | --- | --- | --- |
|
|
142
|
+
| <img src="docs/images/descope.webp" alt="Auth Descope" width="160" /> | Descope | Full OAuth authentication with Descope and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-descope) |
|
|
138
143
|
| <img src="docs/images/showcase-clerk.png" alt="Auth Clerk" width="160" /> | Clerk | Full OAuth authentication with Clerk and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-clerk) |
|
|
139
144
|
| <img src="docs/images/showcase-workos.png" alt="Auth WorkOS AuthKit" width="160" /> | WorkOS AuthKit | Full OAuth authentication with WorkOS AuthKit and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-workos) |
|
|
140
145
|
| <img src="docs/images/showcase-stytch.png" alt="Auth Stytch" width="160" /> | Stytch | Full OAuth authentication with Stytch and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-stytch) |
|
|
141
146
|
| <img src="docs/images/showcase-auth0.png" alt="Auth Auth0" width="160" /> | Auth0 | Full OAuth authentication with Auth0 and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-auth0) |
|
|
147
|
+
| <img src="docs/images/showcase-authplane.png" alt="Auth Authplane" width="160" /> | Authplane | Full OAuth authentication with Authplane and personalized coffee shop search. | [View code](https://github.com/alpic-ai/skybridge/tree/main/examples/auth-authplane) |
|
|
142
148
|
|
|
143
149
|
### UI and component libraries
|
|
144
150
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const ENTRY_WRAPPER_CONTENT = "import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\nimport skills from \"./skills.js\";\n\n__setBuildManifest(manifest);\n__setSkillsManifest(skills);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n";
|
|
2
|
+
export declare function emitEntryWrapper(distDir: string): void;
|
|
3
|
+
export declare function emitSkillsModule(skillsDir: string, outPath: string): void;
|
|
4
|
+
export declare function emitManifestModule(manifestPath: string, outPath: string): void;
|
|
5
|
+
/** Emit an empty Vite manifest for view-less (headless) servers. */
|
|
6
|
+
export declare function emitEmptyManifestModule(outPath: string): void;
|
|
7
|
+
export declare function ensureAssetsDir(assetsDir: string): void;
|
|
8
|
+
export declare const VERCEL_FUNCTION_NAME = "mcp";
|
|
9
|
+
export declare const VERCEL_CONFIG: unknown;
|
|
10
|
+
export declare const VERCEL_VC_CONFIG: unknown;
|
|
11
|
+
export declare function emitVercelBuildOutput(root: string, serverExternal?: string[]): Promise<void>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { discoverSkills } from "../server/skills.js";
|
|
4
|
+
// Primes the manifest and skills snapshot in skybridge's module scope, then
|
|
5
|
+
// dynamically imports `./server.js` so user code runs *after* the side channels
|
|
6
|
+
// are set. The dynamic import is load-bearing: a static
|
|
7
|
+
// `export { default } from ...` is hoisted with the rest of the static graph
|
|
8
|
+
// and would evaluate `server.js` before the primers run. Both `skills.js` and
|
|
9
|
+
// the manifest are imported (not read from disk) so they ride the bundle on
|
|
10
|
+
// filesystem-less targets like Cloudflare Workers.
|
|
11
|
+
export const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest, __setSkillsManifest } from "skybridge/server";
|
|
12
|
+
import manifest from "./vite-manifest.js";
|
|
13
|
+
import skills from "./skills.js";
|
|
14
|
+
|
|
15
|
+
__setBuildManifest(manifest);
|
|
16
|
+
__setSkillsManifest(skills);
|
|
17
|
+
|
|
18
|
+
const userMod = await import("./server.js");
|
|
19
|
+
export default userMod.default;
|
|
20
|
+
`;
|
|
21
|
+
export function emitEntryWrapper(distDir) {
|
|
22
|
+
writeFileSync(path.join(distDir, "__entry.js"), ENTRY_WRAPPER_CONTENT);
|
|
23
|
+
}
|
|
24
|
+
export function emitSkillsModule(skillsDir, outPath) {
|
|
25
|
+
const skills = discoverSkills(skillsDir);
|
|
26
|
+
writeFileSync(outPath, `export default ${JSON.stringify(skills)};\n`);
|
|
27
|
+
}
|
|
28
|
+
export function emitManifestModule(manifestPath, outPath) {
|
|
29
|
+
const manifest = readFileSync(manifestPath, "utf-8");
|
|
30
|
+
writeFileSync(outPath, `export default ${manifest};\n`);
|
|
31
|
+
}
|
|
32
|
+
/** Emit an empty Vite manifest for view-less (headless) servers. */
|
|
33
|
+
export function emitEmptyManifestModule(outPath) {
|
|
34
|
+
writeFileSync(outPath, "export default {};\n");
|
|
35
|
+
}
|
|
36
|
+
export function ensureAssetsDir(assetsDir) {
|
|
37
|
+
mkdirSync(assetsDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
export const VERCEL_FUNCTION_NAME = "mcp";
|
|
40
|
+
export const VERCEL_CONFIG = {
|
|
41
|
+
version: 3,
|
|
42
|
+
routes: [
|
|
43
|
+
{
|
|
44
|
+
src: "/assets/(.*)",
|
|
45
|
+
headers: { "Access-Control-Allow-Origin": "*" },
|
|
46
|
+
continue: true,
|
|
47
|
+
},
|
|
48
|
+
{ handle: "filesystem" },
|
|
49
|
+
{ src: "/(.*)", dest: `/${VERCEL_FUNCTION_NAME}` },
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
export const VERCEL_VC_CONFIG = {
|
|
53
|
+
runtime: "nodejs22.x",
|
|
54
|
+
handler: "index.js",
|
|
55
|
+
launcherType: "Nodejs",
|
|
56
|
+
shouldAddHelpers: true,
|
|
57
|
+
};
|
|
58
|
+
// Emit a Build Output API tree under `.vercel/output/`. Bundling the server
|
|
59
|
+
// with esbuild produces a self-contained function bundle, so we don't ship
|
|
60
|
+
// `node_modules` and don't touch tracked paths like `api/` or `public/`.
|
|
61
|
+
//
|
|
62
|
+
// Entry is `dist/__entry.js` — the wrapper that primes the Vite manifest via
|
|
63
|
+
// `__setBuildManifest` before importing user code. Bundling `dist/server.js`
|
|
64
|
+
// directly would skip that priming and 500 on view resource reads when the
|
|
65
|
+
// function falls back to `readFileSync('dist/assets/.vite/manifest.json')`
|
|
66
|
+
// (Vercel functions don't ship `dist/`).
|
|
67
|
+
export async function emitVercelBuildOutput(root, serverExternal = []) {
|
|
68
|
+
const outputDir = path.join(root, ".vercel", "output");
|
|
69
|
+
const funcDir = path.join(outputDir, "functions", `${VERCEL_FUNCTION_NAME}.func`);
|
|
70
|
+
const staticAssetsDir = path.join(outputDir, "static", "assets");
|
|
71
|
+
rmSync(outputDir, { recursive: true, force: true });
|
|
72
|
+
mkdirSync(funcDir, { recursive: true });
|
|
73
|
+
const { build } = await import("esbuild");
|
|
74
|
+
await build({
|
|
75
|
+
entryPoints: [path.join(root, "dist", "__entry.js")],
|
|
76
|
+
bundle: true,
|
|
77
|
+
platform: "node",
|
|
78
|
+
target: "node22",
|
|
79
|
+
format: "esm",
|
|
80
|
+
outfile: path.join(funcDir, "index.js"),
|
|
81
|
+
// Lets esbuild DCE dev-only branches that pull in vite/devtools.
|
|
82
|
+
define: { "process.env.NODE_ENV": '"production"' },
|
|
83
|
+
// Dev-only deps reachable from re-exports; safe to leave unresolved since
|
|
84
|
+
// the code paths that touch them are eliminated by the NODE_ENV define.
|
|
85
|
+
// `serverExternal` comes from the vite plugin, for deps esbuild can't
|
|
86
|
+
// resolve or load (e.g. optional native modules pulled in transitively).
|
|
87
|
+
external: ["vite", "@skybridge/devtools", ...serverExternal],
|
|
88
|
+
// Native addons can't be inlined into the bundle; copy them next to the
|
|
89
|
+
// function and rewrite the require path to point at the copy.
|
|
90
|
+
loader: { ".node": "file" },
|
|
91
|
+
banner: {
|
|
92
|
+
// ESM bundles miss CJS interop globals that some deps reach for.
|
|
93
|
+
js: "import{createRequire}from'node:module';const require=createRequire(import.meta.url);",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
writeFileSync(path.join(funcDir, ".vc-config.json"), `${JSON.stringify(VERCEL_VC_CONFIG, null, 2)}\n`);
|
|
97
|
+
writeFileSync(path.join(funcDir, "package.json"), `${JSON.stringify({ type: "module" }, null, 2)}\n`);
|
|
98
|
+
const assetsSrc = path.join(root, "dist", "assets");
|
|
99
|
+
if (existsSync(assetsSrc)) {
|
|
100
|
+
cpSync(assetsSrc, staticAssetsDir, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
mkdirSync(staticAssetsDir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
writeFileSync(path.join(outputDir, "config.json"), `${JSON.stringify(VERCEL_CONFIG, null, 2)}\n`);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=build-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-helpers.js","sourceRoot":"","sources":["../../src/cli/build-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { discoverSkills } from \"../server/skills.js\";\n\n// Primes the manifest and skills snapshot in skybridge's module scope, then\n// dynamically imports `./server.js` so user code runs *after* the side channels\n// are set. The dynamic import is load-bearing: a static\n// `export { default } from ...` is hoisted with the rest of the static graph\n// and would evaluate `server.js` before the primers run. Both `skills.js` and\n// the manifest are imported (not read from disk) so they ride the bundle on\n// filesystem-less targets like Cloudflare Workers.\nexport const ENTRY_WRAPPER_CONTENT = `import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\";\nimport manifest from \"./vite-manifest.js\";\nimport skills from \"./skills.js\";\n\n__setBuildManifest(manifest);\n__setSkillsManifest(skills);\n\nconst userMod = await import(\"./server.js\");\nexport default userMod.default;\n`;\n\nexport function emitEntryWrapper(distDir: string): void {\n writeFileSync(path.join(distDir, \"__entry.js\"), ENTRY_WRAPPER_CONTENT);\n}\n\nexport function emitSkillsModule(skillsDir: string, outPath: string): void {\n const skills = discoverSkills(skillsDir);\n writeFileSync(outPath, `export default ${JSON.stringify(skills)};\\n`);\n}\n\nexport function emitManifestModule(\n manifestPath: string,\n outPath: string,\n): void {\n const manifest = readFileSync(manifestPath, \"utf-8\");\n writeFileSync(outPath, `export default ${manifest};\\n`);\n}\n\n/** Emit an empty Vite manifest for view-less (headless) servers. */\nexport function emitEmptyManifestModule(outPath: string): void {\n writeFileSync(outPath, \"export default {};\\n\");\n}\n\nexport function ensureAssetsDir(assetsDir: string): void {\n mkdirSync(assetsDir, { recursive: true });\n}\n\nexport const VERCEL_FUNCTION_NAME = \"mcp\";\n\nexport const VERCEL_CONFIG: unknown = {\n version: 3,\n routes: [\n {\n src: \"/assets/(.*)\",\n headers: { \"Access-Control-Allow-Origin\": \"*\" },\n continue: true,\n },\n { handle: \"filesystem\" },\n { src: \"/(.*)\", dest: `/${VERCEL_FUNCTION_NAME}` },\n ],\n};\n\nexport const VERCEL_VC_CONFIG: unknown = {\n runtime: \"nodejs22.x\",\n handler: \"index.js\",\n launcherType: \"Nodejs\",\n shouldAddHelpers: true,\n};\n\n// Emit a Build Output API tree under `.vercel/output/`. Bundling the server\n// with esbuild produces a self-contained function bundle, so we don't ship\n// `node_modules` and don't touch tracked paths like `api/` or `public/`.\n//\n// Entry is `dist/__entry.js` — the wrapper that primes the Vite manifest via\n// `__setBuildManifest` before importing user code. Bundling `dist/server.js`\n// directly would skip that priming and 500 on view resource reads when the\n// function falls back to `readFileSync('dist/assets/.vite/manifest.json')`\n// (Vercel functions don't ship `dist/`).\nexport async function emitVercelBuildOutput(\n root: string,\n serverExternal: string[] = [],\n): Promise<void> {\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(\n outputDir,\n \"functions\",\n `${VERCEL_FUNCTION_NAME}.func`,\n );\n const staticAssetsDir = path.join(outputDir, \"static\", \"assets\");\n\n rmSync(outputDir, { recursive: true, force: true });\n mkdirSync(funcDir, { recursive: true });\n\n const { build } = await import(\"esbuild\");\n await build({\n entryPoints: [path.join(root, \"dist\", \"__entry.js\")],\n bundle: true,\n platform: \"node\",\n target: \"node22\",\n format: \"esm\",\n outfile: path.join(funcDir, \"index.js\"),\n // Lets esbuild DCE dev-only branches that pull in vite/devtools.\n define: { \"process.env.NODE_ENV\": '\"production\"' },\n // Dev-only deps reachable from re-exports; safe to leave unresolved since\n // the code paths that touch them are eliminated by the NODE_ENV define.\n // `serverExternal` comes from the vite plugin, for deps esbuild can't\n // resolve or load (e.g. optional native modules pulled in transitively).\n external: [\"vite\", \"@skybridge/devtools\", ...serverExternal],\n // Native addons can't be inlined into the bundle; copy them next to the\n // function and rewrite the require path to point at the copy.\n loader: { \".node\": \"file\" },\n banner: {\n // ESM bundles miss CJS interop globals that some deps reach for.\n js: \"import{createRequire}from'node:module';const require=createRequire(import.meta.url);\",\n },\n });\n\n writeFileSync(\n path.join(funcDir, \".vc-config.json\"),\n `${JSON.stringify(VERCEL_VC_CONFIG, null, 2)}\\n`,\n );\n writeFileSync(\n path.join(funcDir, \"package.json\"),\n `${JSON.stringify({ type: \"module\" }, null, 2)}\\n`,\n );\n\n const assetsSrc = path.join(root, \"dist\", \"assets\");\n if (existsSync(assetsSrc)) {\n cpSync(assetsSrc, staticAssetsDir, { recursive: true });\n } else {\n mkdirSync(staticAssetsDir, { recursive: true });\n }\n\n writeFileSync(\n path.join(outputDir, \"config.json\"),\n `${JSON.stringify(VERCEL_CONFIG, null, 2)}\\n`,\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
// esbuild's invariant check on TextEncoder/Uint8Array trips jsdom's polyfill.
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { ENTRY_WRAPPER_CONTENT, emitEmptyManifestModule, emitEntryWrapper, emitManifestModule, emitSkillsModule, emitVercelBuildOutput, ensureAssetsDir, VERCEL_CONFIG, VERCEL_VC_CONFIG, } from "./build-helpers.js";
|
|
8
|
+
function mkTmp(prefix = "skybridge-build-helpers-") {
|
|
9
|
+
return mkdtempSync(path.join(tmpdir(), prefix));
|
|
10
|
+
}
|
|
11
|
+
describe("emitEntryWrapper", () => {
|
|
12
|
+
it("writes dist/__entry.js that primes the manifest before importing user code", () => {
|
|
13
|
+
const dir = mkTmp();
|
|
14
|
+
emitEntryWrapper(dir);
|
|
15
|
+
const out = readFileSync(path.join(dir, "__entry.js"), "utf-8");
|
|
16
|
+
expect(out).toBe(ENTRY_WRAPPER_CONTENT);
|
|
17
|
+
expect(out).toContain('import { __setBuildManifest, __setSkillsManifest } from "skybridge/server"');
|
|
18
|
+
expect(out).toContain('import manifest from "./vite-manifest.js"');
|
|
19
|
+
expect(out).toContain('import skills from "./skills.js"');
|
|
20
|
+
expect(out).toContain("__setBuildManifest(manifest)");
|
|
21
|
+
expect(out).toContain("__setSkillsManifest(skills)");
|
|
22
|
+
// Dynamic import is load-bearing: `server.js` must evaluate after the
|
|
23
|
+
// setters run, so a static re-export wouldn't work.
|
|
24
|
+
expect(out).toContain('await import("./server.js")');
|
|
25
|
+
expect(out).toContain("export default userMod.default");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("emitSkillsModule", () => {
|
|
29
|
+
it("inlines discovered skills as an ESM default export", () => {
|
|
30
|
+
const dir = mkTmp();
|
|
31
|
+
const skillsDir = path.join(dir, "src", "skills");
|
|
32
|
+
mkdirSync(path.join(skillsDir, "refunds"), { recursive: true });
|
|
33
|
+
writeFileSync(path.join(skillsDir, "refunds", "SKILL.md"), "---\nname: refunds\ndescription: Process refunds\n---\nBody");
|
|
34
|
+
const outPath = path.join(dir, "skills.js");
|
|
35
|
+
emitSkillsModule(skillsDir, outPath);
|
|
36
|
+
const out = readFileSync(outPath, "utf-8");
|
|
37
|
+
const literal = out
|
|
38
|
+
.slice("export default ".length)
|
|
39
|
+
.trim()
|
|
40
|
+
.replace(/;$/, "");
|
|
41
|
+
const skills = JSON.parse(literal);
|
|
42
|
+
expect(skills).toHaveLength(1);
|
|
43
|
+
expect(skills[0]).toMatchObject({
|
|
44
|
+
uri: "skill://refunds/SKILL.md",
|
|
45
|
+
frontmatter: { name: "refunds", description: "Process refunds" },
|
|
46
|
+
});
|
|
47
|
+
expect(skills[0].resources).toHaveLength(1);
|
|
48
|
+
expect(skills[0].resources[0].uri).toBe("skill://refunds/SKILL.md");
|
|
49
|
+
expect(skills[0].resources[0].digest).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
50
|
+
});
|
|
51
|
+
it("emits an empty array when the skills directory is absent", () => {
|
|
52
|
+
const dir = mkTmp();
|
|
53
|
+
const outPath = path.join(dir, "skills.js");
|
|
54
|
+
emitSkillsModule(path.join(dir, "nope"), outPath);
|
|
55
|
+
expect(readFileSync(outPath, "utf-8")).toBe("export default [];\n");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("emitManifestModule", () => {
|
|
59
|
+
it("inlines the JSON manifest as an ESM default export", () => {
|
|
60
|
+
const dir = mkTmp();
|
|
61
|
+
const inPath = path.join(dir, "manifest.json");
|
|
62
|
+
const outPath = path.join(dir, "vite-manifest.js");
|
|
63
|
+
const manifest = { "src/views/foo.tsx": { file: "assets/foo-abc.js" } };
|
|
64
|
+
writeFileSync(inPath, JSON.stringify(manifest));
|
|
65
|
+
emitManifestModule(inPath, outPath);
|
|
66
|
+
const out = readFileSync(outPath, "utf-8");
|
|
67
|
+
expect(out.startsWith("export default ")).toBe(true);
|
|
68
|
+
const literal = out
|
|
69
|
+
.slice("export default ".length)
|
|
70
|
+
.trim()
|
|
71
|
+
.replace(/;$/, "");
|
|
72
|
+
expect(JSON.parse(literal)).toEqual(manifest);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe("emitEmptyManifestModule", () => {
|
|
76
|
+
it("writes an empty object default export", () => {
|
|
77
|
+
const dir = mkTmp();
|
|
78
|
+
const outPath = path.join(dir, "vite-manifest.js");
|
|
79
|
+
emitEmptyManifestModule(outPath);
|
|
80
|
+
expect(readFileSync(outPath, "utf-8")).toBe("export default {};\n");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("ensureAssetsDir", () => {
|
|
84
|
+
it("creates the assets directory when missing", () => {
|
|
85
|
+
const dir = mkTmp();
|
|
86
|
+
const assetsDir = path.join(dir, "dist", "assets");
|
|
87
|
+
ensureAssetsDir(assetsDir);
|
|
88
|
+
expect(existsSync(assetsDir)).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe("emitVercelBuildOutput", () => {
|
|
92
|
+
it("emits a Build Output API tree with bundled function and static assets", async () => {
|
|
93
|
+
const root = mkTmp();
|
|
94
|
+
mkdirSync(path.join(root, "dist", "assets"), { recursive: true });
|
|
95
|
+
writeFileSync(path.join(root, "dist", "server.js"), "export default function handler(_req, res) { res.end('ok'); }\n");
|
|
96
|
+
// Minimal `dist/__entry.js` (the real wrapper imports `skybridge/server`,
|
|
97
|
+
// which isn't resolvable in this test's tmp dir — the function-bundling
|
|
98
|
+
// contract we care about here is just "bundle whatever `__entry.js`
|
|
99
|
+
// imports into a single function file").
|
|
100
|
+
writeFileSync(path.join(root, "dist", "__entry.js"), "const userMod = await import('./server.js');\nexport default userMod.default;\n");
|
|
101
|
+
writeFileSync(path.join(root, "dist", "assets", "view-abc.js"), "/* bundled view */\n");
|
|
102
|
+
await emitVercelBuildOutput(root);
|
|
103
|
+
const outputDir = path.join(root, ".vercel", "output");
|
|
104
|
+
const funcDir = path.join(outputDir, "functions", "mcp.func");
|
|
105
|
+
expect(existsSync(path.join(funcDir, "index.js"))).toBe(true);
|
|
106
|
+
expect(JSON.parse(readFileSync(path.join(funcDir, ".vc-config.json"), "utf-8"))).toEqual(VERCEL_VC_CONFIG);
|
|
107
|
+
expect(JSON.parse(readFileSync(path.join(funcDir, "package.json"), "utf-8"))).toEqual({ type: "module" });
|
|
108
|
+
expect(JSON.parse(readFileSync(path.join(outputDir, "config.json"), "utf-8"))).toEqual(VERCEL_CONFIG);
|
|
109
|
+
expect(readFileSync(path.join(outputDir, "static", "assets", "view-abc.js"), "utf-8")).toContain("bundled view");
|
|
110
|
+
});
|
|
111
|
+
it("leaves serverExternal packages unbundled", async () => {
|
|
112
|
+
const root = mkTmp();
|
|
113
|
+
mkdirSync(path.join(root, "dist"), { recursive: true });
|
|
114
|
+
// Unresolvable from the tmp root, so esbuild fails the build unless the
|
|
115
|
+
// package is marked external (bunyan → optional `dtrace-provider`).
|
|
116
|
+
writeFileSync(path.join(root, "dist", "__entry.js"), 'import "dtrace-provider";\nexport default null;\n');
|
|
117
|
+
await expect(emitVercelBuildOutput(root)).rejects.toThrow(/Could not resolve "dtrace-provider"/);
|
|
118
|
+
await emitVercelBuildOutput(root, ["dtrace-provider"]);
|
|
119
|
+
const bundle = readFileSync(path.join(root, ".vercel", "output", "functions", "mcp.func", "index.js"), "utf-8");
|
|
120
|
+
expect(bundle).toContain('import "dtrace-provider"');
|
|
121
|
+
});
|
|
122
|
+
it("emits static assets directory when dist/assets is missing", async () => {
|
|
123
|
+
const root = mkTmp();
|
|
124
|
+
mkdirSync(path.join(root, "dist"), { recursive: true });
|
|
125
|
+
writeFileSync(path.join(root, "dist", "__entry.js"), "const userMod = await import('./server.js');\nexport default userMod.default;\n");
|
|
126
|
+
writeFileSync(path.join(root, "dist", "server.js"), "export default function handler(_req, res) { res.end('ok'); }\n");
|
|
127
|
+
await emitVercelBuildOutput(root);
|
|
128
|
+
expect(existsSync(path.join(root, ".vercel", "output", "static", "assets"))).toBe(true);
|
|
129
|
+
expect(existsSync(path.join(root, ".vercel", "output", "functions", "mcp.func", "index.js"))).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=build-helpers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-helpers.test.js","sourceRoot":"","sources":["../../src/cli/build-helpers.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\n// esbuild's invariant check on TextEncoder/Uint8Array trips jsdom's polyfill.\nimport {\n existsSync,\n mkdirSync,\n mkdtempSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { describe, expect, it } from \"vitest\";\nimport {\n ENTRY_WRAPPER_CONTENT,\n emitEmptyManifestModule,\n emitEntryWrapper,\n emitManifestModule,\n emitSkillsModule,\n emitVercelBuildOutput,\n ensureAssetsDir,\n VERCEL_CONFIG,\n VERCEL_VC_CONFIG,\n} from \"./build-helpers.js\";\n\nfunction mkTmp(prefix = \"skybridge-build-helpers-\") {\n return mkdtempSync(path.join(tmpdir(), prefix));\n}\n\ndescribe(\"emitEntryWrapper\", () => {\n it(\"writes dist/__entry.js that primes the manifest before importing user code\", () => {\n const dir = mkTmp();\n emitEntryWrapper(dir);\n const out = readFileSync(path.join(dir, \"__entry.js\"), \"utf-8\");\n expect(out).toBe(ENTRY_WRAPPER_CONTENT);\n expect(out).toContain(\n 'import { __setBuildManifest, __setSkillsManifest } from \"skybridge/server\"',\n );\n expect(out).toContain('import manifest from \"./vite-manifest.js\"');\n expect(out).toContain('import skills from \"./skills.js\"');\n expect(out).toContain(\"__setBuildManifest(manifest)\");\n expect(out).toContain(\"__setSkillsManifest(skills)\");\n // Dynamic import is load-bearing: `server.js` must evaluate after the\n // setters run, so a static re-export wouldn't work.\n expect(out).toContain('await import(\"./server.js\")');\n expect(out).toContain(\"export default userMod.default\");\n });\n});\n\ndescribe(\"emitSkillsModule\", () => {\n it(\"inlines discovered skills as an ESM default export\", () => {\n const dir = mkTmp();\n const skillsDir = path.join(dir, \"src\", \"skills\");\n mkdirSync(path.join(skillsDir, \"refunds\"), { recursive: true });\n writeFileSync(\n path.join(skillsDir, \"refunds\", \"SKILL.md\"),\n \"---\\nname: refunds\\ndescription: Process refunds\\n---\\nBody\",\n );\n const outPath = path.join(dir, \"skills.js\");\n emitSkillsModule(skillsDir, outPath);\n const out = readFileSync(outPath, \"utf-8\");\n const literal = out\n .slice(\"export default \".length)\n .trim()\n .replace(/;$/, \"\");\n const skills = JSON.parse(literal);\n expect(skills).toHaveLength(1);\n expect(skills[0]).toMatchObject({\n uri: \"skill://refunds/SKILL.md\",\n frontmatter: { name: \"refunds\", description: \"Process refunds\" },\n });\n expect(skills[0].resources).toHaveLength(1);\n expect(skills[0].resources[0].uri).toBe(\"skill://refunds/SKILL.md\");\n expect(skills[0].resources[0].digest).toMatch(/^sha256:[a-f0-9]{64}$/);\n });\n\n it(\"emits an empty array when the skills directory is absent\", () => {\n const dir = mkTmp();\n const outPath = path.join(dir, \"skills.js\");\n emitSkillsModule(path.join(dir, \"nope\"), outPath);\n expect(readFileSync(outPath, \"utf-8\")).toBe(\"export default [];\\n\");\n });\n});\n\ndescribe(\"emitManifestModule\", () => {\n it(\"inlines the JSON manifest as an ESM default export\", () => {\n const dir = mkTmp();\n const inPath = path.join(dir, \"manifest.json\");\n const outPath = path.join(dir, \"vite-manifest.js\");\n const manifest = { \"src/views/foo.tsx\": { file: \"assets/foo-abc.js\" } };\n writeFileSync(inPath, JSON.stringify(manifest));\n emitManifestModule(inPath, outPath);\n const out = readFileSync(outPath, \"utf-8\");\n expect(out.startsWith(\"export default \")).toBe(true);\n const literal = out\n .slice(\"export default \".length)\n .trim()\n .replace(/;$/, \"\");\n expect(JSON.parse(literal)).toEqual(manifest);\n });\n});\n\ndescribe(\"emitEmptyManifestModule\", () => {\n it(\"writes an empty object default export\", () => {\n const dir = mkTmp();\n const outPath = path.join(dir, \"vite-manifest.js\");\n emitEmptyManifestModule(outPath);\n expect(readFileSync(outPath, \"utf-8\")).toBe(\"export default {};\\n\");\n });\n});\n\ndescribe(\"ensureAssetsDir\", () => {\n it(\"creates the assets directory when missing\", () => {\n const dir = mkTmp();\n const assetsDir = path.join(dir, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n expect(existsSync(assetsDir)).toBe(true);\n });\n});\n\ndescribe(\"emitVercelBuildOutput\", () => {\n it(\"emits a Build Output API tree with bundled function and static assets\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\", \"assets\"), { recursive: true });\n writeFileSync(\n path.join(root, \"dist\", \"server.js\"),\n \"export default function handler(_req, res) { res.end('ok'); }\\n\",\n );\n // Minimal `dist/__entry.js` (the real wrapper imports `skybridge/server`,\n // which isn't resolvable in this test's tmp dir — the function-bundling\n // contract we care about here is just \"bundle whatever `__entry.js`\n // imports into a single function file\").\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n \"const userMod = await import('./server.js');\\nexport default userMod.default;\\n\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"assets\", \"view-abc.js\"),\n \"/* bundled view */\\n\",\n );\n\n await emitVercelBuildOutput(root);\n\n const outputDir = path.join(root, \".vercel\", \"output\");\n const funcDir = path.join(outputDir, \"functions\", \"mcp.func\");\n\n expect(existsSync(path.join(funcDir, \"index.js\"))).toBe(true);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \".vc-config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_VC_CONFIG);\n expect(\n JSON.parse(readFileSync(path.join(funcDir, \"package.json\"), \"utf-8\")),\n ).toEqual({ type: \"module\" });\n expect(\n JSON.parse(readFileSync(path.join(outputDir, \"config.json\"), \"utf-8\")),\n ).toEqual(VERCEL_CONFIG);\n expect(\n readFileSync(\n path.join(outputDir, \"static\", \"assets\", \"view-abc.js\"),\n \"utf-8\",\n ),\n ).toContain(\"bundled view\");\n });\n\n it(\"leaves serverExternal packages unbundled\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\"), { recursive: true });\n // Unresolvable from the tmp root, so esbuild fails the build unless the\n // package is marked external (bunyan → optional `dtrace-provider`).\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n 'import \"dtrace-provider\";\\nexport default null;\\n',\n );\n\n await expect(emitVercelBuildOutput(root)).rejects.toThrow(\n /Could not resolve \"dtrace-provider\"/,\n );\n await emitVercelBuildOutput(root, [\"dtrace-provider\"]);\n\n const bundle = readFileSync(\n path.join(root, \".vercel\", \"output\", \"functions\", \"mcp.func\", \"index.js\"),\n \"utf-8\",\n );\n expect(bundle).toContain('import \"dtrace-provider\"');\n });\n\n it(\"emits static assets directory when dist/assets is missing\", async () => {\n const root = mkTmp();\n mkdirSync(path.join(root, \"dist\"), { recursive: true });\n writeFileSync(\n path.join(root, \"dist\", \"__entry.js\"),\n \"const userMod = await import('./server.js');\\nexport default userMod.default;\\n\",\n );\n writeFileSync(\n path.join(root, \"dist\", \"server.js\"),\n \"export default function handler(_req, res) { res.end('ok'); }\\n\",\n );\n\n await emitVercelBuildOutput(root);\n\n expect(\n existsSync(path.join(root, \".vercel\", \"output\", \"static\", \"assets\")),\n ).toBe(true);\n expect(\n existsSync(\n path.join(\n root,\n \".vercel\",\n \"output\",\n \"functions\",\n \"mcp.func\",\n \"index.js\",\n ),\n ),\n ).toBe(true);\n });\n});\n"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path, { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { scanAndWriteViewsDts, scanViewsSync, } from "../web/plugin/scan-views.js";
|
|
4
|
+
import { emitEmptyManifestModule, emitEntryWrapper, emitManifestModule, emitSkillsModule, emitVercelBuildOutput, ensureAssetsDir, } from "./build-helpers.js";
|
|
5
|
+
import { resolveSkybridgeConfig } from "./resolve-skybridge-config.js";
|
|
6
|
+
export async function getCommandSteps(root = process.cwd()) {
|
|
7
|
+
const { viewsDir, serverExternal } = await resolveSkybridgeConfig(root);
|
|
8
|
+
const rawDir = viewsDir ?? "src/views";
|
|
9
|
+
const resolvedDir = isAbsolute(rawDir) ? rawDir : resolve(root, rawDir);
|
|
10
|
+
// Non-throwing pre-check: duplicate view names are validated in the
|
|
11
|
+
// "Scanning views" step so useExecuteSteps can render a styled error.
|
|
12
|
+
const hasViews = scanViewsSync(resolvedDir).valid.length > 0;
|
|
13
|
+
const steps = [
|
|
14
|
+
{
|
|
15
|
+
label: "Scanning views",
|
|
16
|
+
run: async () => {
|
|
17
|
+
scanAndWriteViewsDts(root, viewsDir);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Compiling server",
|
|
22
|
+
run: () => rmSync(path.join(root, "dist"), { recursive: true, force: true }),
|
|
23
|
+
command: "tsc -b --force",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
if (hasViews) {
|
|
27
|
+
steps.push({
|
|
28
|
+
label: "Building views",
|
|
29
|
+
command: "vite build",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
steps.push({
|
|
33
|
+
label: "Emitting manifest module",
|
|
34
|
+
run: () => {
|
|
35
|
+
const manifestOut = path.join(root, "dist", "vite-manifest.js");
|
|
36
|
+
if (hasViews) {
|
|
37
|
+
emitManifestModule(path.join(root, "dist", "assets", ".vite", "manifest.json"), manifestOut);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
emitEmptyManifestModule(manifestOut);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
}, {
|
|
44
|
+
label: "Emitting skills module",
|
|
45
|
+
run: () => {
|
|
46
|
+
emitSkillsModule(resolve(root, "src/skills"), path.join(root, "dist", "skills.js"));
|
|
47
|
+
},
|
|
48
|
+
}, {
|
|
49
|
+
label: "Emitting entry wrapper",
|
|
50
|
+
run: () => {
|
|
51
|
+
emitEntryWrapper(path.join(root, "dist"));
|
|
52
|
+
},
|
|
53
|
+
}, {
|
|
54
|
+
label: "Emitting Cloudflare redirects",
|
|
55
|
+
run: () => {
|
|
56
|
+
const assetsDir = path.join(root, "dist", "assets");
|
|
57
|
+
ensureAssetsDir(assetsDir);
|
|
58
|
+
writeFileSync(path.join(assetsDir, "_redirects"), "/assets/assets/* /assets/:splat 200\n");
|
|
59
|
+
},
|
|
60
|
+
}, {
|
|
61
|
+
label: "Emitting Cloudflare headers",
|
|
62
|
+
run: () => {
|
|
63
|
+
const assetsDir = path.join(root, "dist", "assets");
|
|
64
|
+
ensureAssetsDir(assetsDir);
|
|
65
|
+
writeFileSync(path.join(assetsDir, "_headers"), "/assets/*\n Access-Control-Allow-Origin: *\n");
|
|
66
|
+
},
|
|
67
|
+
}, {
|
|
68
|
+
label: "Emitting Vercel build output",
|
|
69
|
+
run: () => emitVercelBuildOutput(root, serverExternal),
|
|
70
|
+
});
|
|
71
|
+
return steps;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=build-steps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-steps.js","sourceRoot":"","sources":["../../src/cli/build-steps.ts"],"names":[],"mappings":"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","sourcesContent":["import { rmSync, writeFileSync } from \"node:fs\";\nimport path, { isAbsolute, resolve } from \"node:path\";\nimport {\n scanAndWriteViewsDts,\n scanViewsSync,\n} from \"../web/plugin/scan-views.js\";\nimport {\n emitEmptyManifestModule,\n emitEntryWrapper,\n emitManifestModule,\n emitSkillsModule,\n emitVercelBuildOutput,\n ensureAssetsDir,\n} from \"./build-helpers.js\";\nimport { resolveSkybridgeConfig } from \"./resolve-skybridge-config.js\";\nimport type { CommandStep } from \"./use-execute-steps.js\";\n\nexport async function getCommandSteps(\n root = process.cwd(),\n): Promise<CommandStep[]> {\n const { viewsDir, serverExternal } = await resolveSkybridgeConfig(root);\n const rawDir = viewsDir ?? \"src/views\";\n const resolvedDir = isAbsolute(rawDir) ? rawDir : resolve(root, rawDir);\n // Non-throwing pre-check: duplicate view names are validated in the\n // \"Scanning views\" step so useExecuteSteps can render a styled error.\n const hasViews = scanViewsSync(resolvedDir).valid.length > 0;\n\n const steps: CommandStep[] = [\n {\n label: \"Scanning views\",\n run: async () => {\n scanAndWriteViewsDts(root, viewsDir);\n },\n },\n {\n label: \"Compiling server\",\n run: () =>\n rmSync(path.join(root, \"dist\"), { recursive: true, force: true }),\n command: \"tsc -b --force\",\n },\n ];\n\n if (hasViews) {\n steps.push({\n label: \"Building views\",\n command: \"vite build\",\n });\n }\n\n steps.push(\n {\n label: \"Emitting manifest module\",\n run: () => {\n const manifestOut = path.join(root, \"dist\", \"vite-manifest.js\");\n if (hasViews) {\n emitManifestModule(\n path.join(root, \"dist\", \"assets\", \".vite\", \"manifest.json\"),\n manifestOut,\n );\n } else {\n emitEmptyManifestModule(manifestOut);\n }\n },\n },\n {\n label: \"Emitting skills module\",\n run: () => {\n emitSkillsModule(\n resolve(root, \"src/skills\"),\n path.join(root, \"dist\", \"skills.js\"),\n );\n },\n },\n {\n label: \"Emitting entry wrapper\",\n run: () => {\n emitEntryWrapper(path.join(root, \"dist\"));\n },\n },\n {\n label: \"Emitting Cloudflare redirects\",\n run: () => {\n const assetsDir = path.join(root, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n writeFileSync(\n path.join(assetsDir, \"_redirects\"),\n \"/assets/assets/* /assets/:splat 200\\n\",\n );\n },\n },\n {\n label: \"Emitting Cloudflare headers\",\n run: () => {\n const assetsDir = path.join(root, \"dist\", \"assets\");\n ensureAssetsDir(assetsDir);\n writeFileSync(\n path.join(assetsDir, \"_headers\"),\n \"/assets/*\\n Access-Control-Allow-Origin: *\\n\",\n );\n },\n },\n {\n label: \"Emitting Vercel build output\",\n run: () => emitVercelBuildOutput(root, serverExternal),\n },\n );\n\n return steps;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync, } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { getCommandSteps } from "./build-steps.js";
|
|
7
|
+
function mkTmp(prefix = "skybridge-build-steps-") {
|
|
8
|
+
return mkdtempSync(path.join(tmpdir(), prefix));
|
|
9
|
+
}
|
|
10
|
+
describe("getCommandSteps", () => {
|
|
11
|
+
it("omits the vite build step when no views are registered", async () => {
|
|
12
|
+
const root = mkTmp();
|
|
13
|
+
mkdirSync(path.join(root, "src"), { recursive: true });
|
|
14
|
+
writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
|
|
15
|
+
import { skybridge } from "skybridge/vite";
|
|
16
|
+
export default defineConfig({ plugins: [skybridge()] });`);
|
|
17
|
+
writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify({ compilerOptions: { strict: true } }));
|
|
18
|
+
const steps = await getCommandSteps(root);
|
|
19
|
+
const labels = steps.map((step) => step.label);
|
|
20
|
+
expect(labels).not.toContain("Building views");
|
|
21
|
+
expect(labels).toContain("Emitting manifest module");
|
|
22
|
+
expect(labels.at(-1)).toBe("Emitting Vercel build output");
|
|
23
|
+
});
|
|
24
|
+
it("includes the vite build step when views are present", async () => {
|
|
25
|
+
const root = mkTmp();
|
|
26
|
+
mkdirSync(path.join(root, "src", "views"), { recursive: true });
|
|
27
|
+
writeFileSync(path.join(root, "src", "views", "hello.tsx"), "export default function Hello() { return null; }\n");
|
|
28
|
+
writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
|
|
29
|
+
import { skybridge } from "skybridge/vite";
|
|
30
|
+
export default defineConfig({ plugins: [skybridge()] });`);
|
|
31
|
+
writeFileSync(path.join(root, "tsconfig.json"), JSON.stringify({ compilerOptions: { strict: true } }));
|
|
32
|
+
const steps = await getCommandSteps(root);
|
|
33
|
+
const labels = steps.map((step) => step.label);
|
|
34
|
+
expect(labels).toContain("Building views");
|
|
35
|
+
});
|
|
36
|
+
it("emits an empty manifest module when there are no views", async () => {
|
|
37
|
+
const root = mkTmp();
|
|
38
|
+
mkdirSync(path.join(root, "src"), { recursive: true });
|
|
39
|
+
writeFileSync(path.join(root, "vite.config.ts"), `import { defineConfig } from "vite";
|
|
40
|
+
import { skybridge } from "skybridge/vite";
|
|
41
|
+
export default defineConfig({ plugins: [skybridge()] });`);
|
|
42
|
+
const steps = await getCommandSteps(root);
|
|
43
|
+
const emitManifest = steps.find((step) => step.label === "Emitting manifest module");
|
|
44
|
+
expect(emitManifest?.run).toBeDefined();
|
|
45
|
+
mkdirSync(path.join(root, "dist"), { recursive: true });
|
|
46
|
+
await emitManifest?.run?.();
|
|
47
|
+
const manifestOut = path.join(root, "dist", "vite-manifest.js");
|
|
48
|
+
expect(existsSync(manifestOut)).toBe(true);
|
|
49
|
+
expect(readFileSync(manifestOut, "utf-8")).toBe("export default {};\n");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=build-steps.test.js.map
|