skybridge 0.0.0-dev.ff4b4a2 → 0.0.0-dev.ffaef7a
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 +124 -115
- package/dist/cli/build-helpers.d.ts +10 -0
- package/dist/cli/build-helpers.js +93 -0
- package/dist/cli/build-helpers.js.map +1 -0
- package/dist/cli/build-helpers.test.js +89 -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 +68 -0
- package/dist/cli/build-steps.js.map +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 +18 -0
- package/dist/cli/detect-port.js +50 -0
- package/dist/cli/detect-port.js.map +1 -0
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/header.js +1 -1
- package/dist/cli/header.js.map +1 -1
- package/dist/cli/resolve-views-dir.d.ts +1 -0
- package/dist/cli/resolve-views-dir.js +17 -0
- package/dist/cli/resolve-views-dir.js.map +1 -0
- package/dist/cli/run-command.js.map +1 -1
- 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/telemetry.js.map +1 -1
- package/dist/cli/tunnel-control-server.d.ts +9 -0
- package/dist/cli/tunnel-control-server.js +31 -0
- package/dist/cli/tunnel-control-server.js.map +1 -0
- package/dist/cli/tunnel-control-server.test.d.ts +1 -0
- package/dist/cli/tunnel-control-server.test.js +39 -0
- package/dist/cli/tunnel-control-server.test.js.map +1 -0
- package/dist/cli/tunnel-handler.d.ts +3 -0
- package/dist/cli/tunnel-handler.js +48 -0
- package/dist/cli/tunnel-handler.js.map +1 -0
- package/dist/cli/tunnel-handler.test.d.ts +1 -0
- package/dist/cli/tunnel-handler.test.js +105 -0
- package/dist/cli/tunnel-handler.test.js.map +1 -0
- package/dist/cli/tunnel.d.ts +57 -0
- package/dist/cli/tunnel.js +154 -0
- package/dist/cli/tunnel.js.map +1 -0
- package/dist/cli/tunnel.test.d.ts +1 -0
- package/dist/cli/tunnel.test.js +190 -0
- package/dist/cli/tunnel.test.js.map +1 -0
- package/dist/cli/types.d.ts +5 -0
- package/dist/cli/types.js +2 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/cli/use-execute-steps.d.ts +1 -1
- package/dist/cli/use-execute-steps.js.map +1 -1
- package/dist/cli/use-messages.d.ts +3 -0
- package/dist/cli/use-messages.js +11 -0
- package/dist/cli/use-messages.js.map +1 -0
- package/dist/cli/use-nodemon.d.ts +16 -6
- package/dist/cli/use-nodemon.js +77 -54
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-open-browser.d.ts +1 -0
- package/dist/cli/use-open-browser.js +44 -0
- package/dist/cli/use-open-browser.js.map +1 -0
- package/dist/cli/use-tunnel.d.ts +14 -0
- package/dist/cli/use-tunnel.js +131 -0
- package/dist/cli/use-tunnel.js.map +1 -0
- package/dist/cli/use-typescript-check.d.ts +9 -2
- package/dist/cli/use-typescript-check.js +89 -51
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -3
- package/dist/commands/build.js +3 -18
- 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 +5 -1
- package/dist/commands/dev.js +105 -8
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts +3 -1
- package/dist/commands/start.js +37 -15
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/telemetry/disable.js.map +1 -1
- package/dist/commands/telemetry/enable.js.map +1 -1
- package/dist/commands/telemetry/status.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.d.ts +6 -6
- package/dist/server/asset-base-url-transform-plugin.js +25 -11
- package/dist/server/asset-base-url-transform-plugin.js.map +1 -1
- package/dist/server/asset-base-url-transform-plugin.test.js +92 -14
- 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 +18 -0
- package/dist/server/auth/index.js +2 -0
- package/dist/server/auth/index.js.map +1 -0
- package/dist/server/auth/providers/auth0.d.ts +18 -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 +48 -0
- package/dist/server/auth/providers/auth0.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +14 -0
- package/dist/server/auth/providers/clerk.js +16 -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 +28 -0
- package/dist/server/auth/providers/clerk.test.js.map +1 -0
- package/dist/server/auth/providers/custom.d.ts +24 -0
- package/dist/server/auth/providers/custom.js +37 -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 +107 -0
- package/dist/server/auth/providers/custom.test.js.map +1 -0
- package/dist/server/auth/providers/descope.d.ts +15 -0
- package/dist/server/auth/providers/descope.js +33 -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 +37 -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 +12 -0
- package/dist/server/auth/providers/stytch.js +13 -0
- package/dist/server/auth/providers/stytch.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +11 -0
- package/dist/server/auth/providers/workos.js +12 -0
- package/dist/server/auth/providers/workos.js.map +1 -0
- package/dist/server/auth/security-schemes.d.ts +13 -0
- package/dist/server/auth/security-schemes.js +53 -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 +90 -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 +98 -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 +450 -0
- package/dist/server/auth/setup.test.js.map +1 -0
- package/dist/server/auth/verify.d.ts +12 -0
- package/dist/server/auth/verify.js +38 -0
- package/dist/server/auth/verify.js.map +1 -0
- package/dist/server/auth/verify.test.d.ts +1 -0
- package/dist/server/auth/verify.test.js +100 -0
- package/dist/server/auth/verify.test.js.map +1 -0
- package/dist/server/auth.d.ts +20 -0
- package/dist/server/auth.js +28 -0
- package/dist/server/auth.js.map +1 -0
- package/dist/server/build-manifest.test.d.ts +1 -0
- package/dist/server/build-manifest.test.js +27 -0
- package/dist/server/build-manifest.test.js.map +1 -0
- package/dist/server/content-helpers.d.ts +67 -0
- package/dist/server/content-helpers.js +79 -0
- package/dist/server/content-helpers.js.map +1 -0
- package/dist/server/content-helpers.test.d.ts +1 -0
- package/dist/server/content-helpers.test.js +70 -0
- package/dist/server/content-helpers.test.js.map +1 -0
- package/dist/server/express.d.ts +11 -0
- package/dist/server/express.js +101 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/express.test.d.ts +1 -0
- package/dist/server/express.test.js +491 -0
- package/dist/server/express.test.js.map +1 -0
- package/dist/server/file-ref.d.ts +28 -0
- package/dist/server/file-ref.js +27 -0
- package/dist/server/file-ref.js.map +1 -0
- package/dist/server/index.d.ts +14 -3
- package/dist/server/index.js +11 -2
- package/dist/server/index.js.map +1 -1
- package/dist/server/inferUtilityTypes.d.ts +6 -6
- package/dist/server/inferUtilityTypes.js.map +1 -1
- package/dist/server/metric.d.ts +14 -0
- package/dist/server/metric.js +62 -0
- package/dist/server/metric.js.map +1 -0
- package/dist/server/middleware.d.ts +137 -0
- package/dist/server/middleware.js +93 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/middleware.test-d.d.ts +1 -0
- package/dist/server/middleware.test-d.js +75 -0
- package/dist/server/middleware.test-d.js.map +1 -0
- package/dist/server/middleware.test.d.ts +1 -0
- package/dist/server/middleware.test.js +493 -0
- package/dist/server/middleware.test.js.map +1 -0
- 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 +391 -58
- package/dist/server/server.js +644 -111
- package/dist/server/server.js.map +1 -1
- package/dist/server/templateHelper.d.ts +5 -8
- package/dist/server/templateHelper.js +3 -22
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.d.ts +4 -0
- package/dist/server/templates.generated.js +47 -0
- package/dist/server/templates.generated.js.map +1 -0
- package/dist/server/tunnel-proxy-router.d.ts +7 -0
- package/dist/server/tunnel-proxy-router.js +110 -0
- package/dist/server/tunnel-proxy-router.js.map +1 -0
- package/dist/server/tunnel-proxy-router.test.d.ts +1 -0
- package/dist/server/tunnel-proxy-router.test.js +229 -0
- package/dist/server/tunnel-proxy-router.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.d.ts +14 -0
- package/dist/server/viewsDevServer.js +45 -0
- package/dist/server/viewsDevServer.js.map +1 -0
- package/dist/test/utils.d.ts +13 -21
- package/dist/test/utils.js +42 -37
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.d.ts +1 -0
- package/dist/test/view.test.js +536 -0
- package/dist/test/view.test.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/dist/web/bridges/adaptor.d.ts +51 -0
- package/dist/web/bridges/adaptor.js +330 -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 +208 -0
- package/dist/web/bridges/adaptor.test.js.map +1 -0
- package/dist/web/bridges/apps-sdk/bridge.d.ts +7 -2
- 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 +1 -2
- 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 +32 -14
- 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/index.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +27 -31
- package/dist/web/bridges/mcp-app/bridge.js +109 -198
- 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.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +17 -3
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +14 -2
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -41
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.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 +130 -14
- 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 +5 -6
- 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 +43 -3
- package/dist/web/create-store.js.map +1 -1
- package/dist/web/create-store.test.js +31 -25
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +35 -2
- package/dist/web/data-llm.js +31 -3
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +50 -35
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +22 -18
- package/dist/web/generate-helpers.js +22 -18
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +30 -28
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/helpers/state.d.ts +2 -2
- package/dist/web/helpers/state.js +11 -11
- package/dist/web/helpers/state.js.map +1 -1
- package/dist/web/helpers/state.test.js +9 -9
- package/dist/web/helpers/state.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +6 -2
- package/dist/web/hooks/index.js +5 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/test/utils.d.ts +6 -2
- package/dist/web/hooks/test/utils.js +17 -2
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +45 -0
- package/dist/web/hooks/use-call-tool.js +28 -0
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test-d.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +62 -27
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-display-mode.d.ts +23 -3
- 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-d.d.ts +1 -0
- package/dist/web/hooks/use-display-mode.test-d.js +8 -0
- package/dist/web/hooks/use-display-mode.test-d.js.map +1 -0
- 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 +34 -1
- package/dist/web/hooks/use-files.js +33 -0
- package/dist/web/hooks/use-files.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +35 -3
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-layout.d.ts +2 -0
- package/dist/web/hooks/use-layout.js +2 -0
- package/dist/web/hooks/use-layout.js.map +1 -1
- package/dist/web/hooks/use-layout.test.js +62 -29
- package/dist/web/hooks/use-layout.test.js.map +1 -1
- package/dist/web/hooks/use-open-external.d.ts +20 -1
- package/dist/web/hooks/use-open-external.js +17 -1
- package/dist/web/hooks/use-open-external.js.map +1 -1
- package/dist/web/hooks/use-open-external.test.js +38 -13
- 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 +19 -4
- package/dist/web/hooks/use-request-modal.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +15 -1
- 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 +19 -1
- package/dist/web/hooks/use-send-follow-up-message.js +19 -2
- 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 +30 -16
- 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 +43 -33
- 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 +20 -2
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +101 -27
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.d.ts +25 -0
- package/dist/web/hooks/use-view-state.js +32 -0
- package/dist/web/hooks/use-view-state.js.map +1 -0
- package/dist/web/hooks/use-view-state.test.d.ts +1 -0
- package/dist/web/hooks/use-view-state.test.js +181 -0
- package/dist/web/hooks/use-view-state.test.js.map +1 -0
- package/dist/web/index.d.ts +1 -2
- package/dist/web/index.js +1 -2
- package/dist/web/index.js.map +1 -1
- package/dist/web/mount-view.d.ts +20 -0
- package/dist/web/{mount-widget.js → mount-view.js} +21 -2
- package/dist/web/mount-view.js.map +1 -0
- package/dist/web/plugin/data-llm.test.js.map +1 -1
- package/dist/web/plugin/plugin.d.ts +32 -1
- package/dist/web/plugin/plugin.js +160 -18
- package/dist/web/plugin/plugin.js.map +1 -1
- package/dist/web/plugin/scan-views.d.ts +16 -0
- package/dist/web/plugin/scan-views.js +88 -0
- package/dist/web/plugin/scan-views.js.map +1 -0
- package/dist/web/plugin/scan-views.test.d.ts +1 -0
- package/dist/web/plugin/scan-views.test.js +99 -0
- package/dist/web/plugin/scan-views.test.js.map +1 -0
- package/dist/web/plugin/transform-data-llm.js +1 -1
- package/dist/web/plugin/transform-data-llm.js.map +1 -1
- package/dist/web/plugin/transform-data-llm.test.js.map +1 -1
- package/dist/web/plugin/validate-view.d.ts +1 -0
- package/dist/web/plugin/validate-view.js +9 -0
- package/dist/web/plugin/validate-view.js.map +1 -0
- package/dist/web/plugin/validate-view.test.d.ts +1 -0
- package/dist/web/plugin/validate-view.test.js +24 -0
- package/dist/web/plugin/validate-view.test.js.map +1 -0
- package/dist/web/proxy.js.map +1 -1
- package/dist/web/types.d.ts +4 -0
- package/dist/web/types.js.map +1 -1
- package/package.json +51 -30
- package/tsconfig.base.json +33 -0
- package/dist/server/templates/development.hbs +0 -67
- package/dist/server/templates/production.hbs +0 -6
- package/dist/server/widgetsDevServer.d.ts +0 -12
- package/dist/server/widgetsDevServer.js +0 -57
- package/dist/server/widgetsDevServer.js.map +0 -1
- package/dist/test/widget.test.js +0 -255
- package/dist/test/widget.test.js.map +0 -1
- package/dist/web/bridges/apps-sdk/adaptor.d.ts +0 -22
- package/dist/web/bridges/apps-sdk/adaptor.js +0 -64
- package/dist/web/bridges/apps-sdk/adaptor.js.map +0 -1
- package/dist/web/bridges/mcp-app/adaptor.d.ts +0 -36
- package/dist/web/bridges/mcp-app/adaptor.js +0 -185
- package/dist/web/bridges/mcp-app/adaptor.js.map +0 -1
- package/dist/web/hooks/use-widget-state.d.ts +0 -4
- package/dist/web/hooks/use-widget-state.js +0 -32
- package/dist/web/hooks/use-widget-state.js.map +0 -1
- package/dist/web/hooks/use-widget-state.test.js +0 -61
- package/dist/web/hooks/use-widget-state.test.js.map +0 -1
- package/dist/web/mount-widget.d.ts +0 -1
- package/dist/web/mount-widget.js.map +0 -1
- /package/dist/{test/widget.test.d.ts → cli/build-helpers.test.d.ts} +0 -0
- /package/dist/{web/hooks/use-widget-state.test.d.ts → cli/build-steps.test.d.ts} +0 -0
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { assetBaseUrlTransform } from "./asset-base-url-transform-plugin.js";
|
|
2
|
+
import { assetBaseUrlTransform, assetBaseUrlTransformPlugin, isCssRequest, } from "./asset-base-url-transform-plugin.js";
|
|
3
3
|
describe("assetBaseUrlTransform", () => {
|
|
4
|
-
|
|
5
|
-
it("should transform asset paths in single, double, and backtick quotes", () => {
|
|
4
|
+
it("should transform asset paths to use window.skybridge.serverUrl", () => {
|
|
6
5
|
const cases = [
|
|
7
6
|
{
|
|
8
7
|
desc: "single-quoted",
|
|
9
8
|
code: `const image = '/assets/logo.png';`,
|
|
10
|
-
expected: `const image =
|
|
9
|
+
expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
|
|
11
10
|
},
|
|
12
11
|
{
|
|
13
12
|
desc: "double-quoted",
|
|
14
13
|
code: `const image = "/assets/logo.png";`,
|
|
15
|
-
expected: `const image = "
|
|
14
|
+
expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
|
|
16
15
|
},
|
|
17
16
|
{
|
|
18
17
|
desc: "backtick-quoted",
|
|
19
18
|
code: "const image = `/assets/logo.png`;",
|
|
20
|
-
expected: `const image =
|
|
19
|
+
expected: `const image = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`,
|
|
21
20
|
},
|
|
22
21
|
];
|
|
23
22
|
for (const { code, expected } of cases) {
|
|
24
|
-
const result = assetBaseUrlTransform(code
|
|
23
|
+
const result = assetBaseUrlTransform(code);
|
|
25
24
|
expect(result).toBe(expected);
|
|
26
25
|
}
|
|
27
26
|
});
|
|
@@ -31,10 +30,10 @@ describe("assetBaseUrlTransform", () => {
|
|
|
31
30
|
const icon = '/assets/icon.svg';
|
|
32
31
|
const font = '/assets/font.woff2';
|
|
33
32
|
`;
|
|
34
|
-
const result = assetBaseUrlTransform(code
|
|
35
|
-
expect(result).toContain(
|
|
36
|
-
expect(result).toContain(
|
|
37
|
-
expect(result).toContain(
|
|
33
|
+
const result = assetBaseUrlTransform(code);
|
|
34
|
+
expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/logo.png"`);
|
|
35
|
+
expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/icon.svg"`);
|
|
36
|
+
expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/font.woff2"`);
|
|
38
37
|
});
|
|
39
38
|
it("should not transform already absolute URLs", () => {
|
|
40
39
|
const code = `
|
|
@@ -42,15 +41,94 @@ describe("assetBaseUrlTransform", () => {
|
|
|
42
41
|
const http = 'http://example.com/image.png';
|
|
43
42
|
const https = 'https://example.com/image.png';
|
|
44
43
|
`;
|
|
45
|
-
const result = assetBaseUrlTransform(code
|
|
46
|
-
expect(result).toContain(
|
|
44
|
+
const result = assetBaseUrlTransform(code);
|
|
45
|
+
expect(result).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/logo.png"`);
|
|
47
46
|
expect(result).toContain("http://example.com/image.png");
|
|
48
47
|
expect(result).toContain("https://example.com/image.png");
|
|
49
48
|
});
|
|
50
49
|
it("should not transform code without asset paths", () => {
|
|
51
50
|
const code = `const text = "Hello World";`;
|
|
52
|
-
const result = assetBaseUrlTransform(code
|
|
51
|
+
const result = assetBaseUrlTransform(code);
|
|
53
52
|
expect(result).toBe(code);
|
|
54
53
|
});
|
|
54
|
+
it("should not transform asset paths inside static `import ... from` clauses", () => {
|
|
55
|
+
// Reproducer for #713: a dep does `import * as sprite from './icons.svg'`,
|
|
56
|
+
// Vite resolves the relative path to absolute, then this transform used
|
|
57
|
+
// to rewrite the resolved string — producing invalid JS like
|
|
58
|
+
// `import * as sprite from (expr) + "..."` that crashes vite:import-analysis.
|
|
59
|
+
const cases = [
|
|
60
|
+
`import * as sprite from "/Users/me/proj/node_modules/pkg/icons.svg";`,
|
|
61
|
+
`import sprite from '/assets/icons.svg';`,
|
|
62
|
+
`import sprite from "/assets/icons.svg";`,
|
|
63
|
+
`export { default } from "/assets/icons.svg";`,
|
|
64
|
+
`export * from '/assets/sprites.svg';`,
|
|
65
|
+
];
|
|
66
|
+
for (const code of cases) {
|
|
67
|
+
expect(assetBaseUrlTransform(code)).toBe(code);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
it("should still transform value-position asset paths in files that also have unrelated imports", () => {
|
|
71
|
+
const code = [
|
|
72
|
+
`import { foo } from "./foo.js";`,
|
|
73
|
+
`import * as sprite from "/assets/sprite.svg";`,
|
|
74
|
+
`const logo = "/assets/logo.png";`,
|
|
75
|
+
].join("\n");
|
|
76
|
+
const result = assetBaseUrlTransform(code);
|
|
77
|
+
// Imports untouched
|
|
78
|
+
expect(result).toContain(`from "./foo.js"`);
|
|
79
|
+
expect(result).toContain(`from "/assets/sprite.svg"`);
|
|
80
|
+
// Value-position rewritten
|
|
81
|
+
expect(result).toContain(`const logo = (window.skybridge?.serverUrl ?? "") + "/assets/logo.png";`);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("isCssRequest", () => {
|
|
85
|
+
it("returns true for CSS-family extensions", () => {
|
|
86
|
+
expect(isCssRequest("/src/styles.css")).toBe(true);
|
|
87
|
+
expect(isCssRequest("/src/styles.module.css")).toBe(true);
|
|
88
|
+
expect(isCssRequest("/src/styles.scss")).toBe(true);
|
|
89
|
+
expect(isCssRequest("/src/styles.sass")).toBe(true);
|
|
90
|
+
expect(isCssRequest("/src/styles.less")).toBe(true);
|
|
91
|
+
expect(isCssRequest("/src/styles.styl")).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
it("returns true for CSS modules with Vite query strings", () => {
|
|
94
|
+
expect(isCssRequest("/src/styles.css?direct")).toBe(true);
|
|
95
|
+
expect(isCssRequest("/src/styles.css?inline")).toBe(true);
|
|
96
|
+
expect(isCssRequest("/src/styles.css?used")).toBe(true);
|
|
97
|
+
expect(isCssRequest("/src/Foo.vue?vue&type=style&lang.css")).toBe(true);
|
|
98
|
+
});
|
|
99
|
+
it("returns false for non-CSS modules", () => {
|
|
100
|
+
expect(isCssRequest("/src/index.tsx")).toBe(false);
|
|
101
|
+
expect(isCssRequest("/src/utils.ts")).toBe(false);
|
|
102
|
+
expect(isCssRequest("/src/Logo.svg")).toBe(false);
|
|
103
|
+
expect(isCssRequest("/src/notes.cssx")).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe("assetBaseUrlTransformPlugin", () => {
|
|
107
|
+
function runTransform(id, code) {
|
|
108
|
+
const plugin = assetBaseUrlTransformPlugin();
|
|
109
|
+
const hook = plugin.transform;
|
|
110
|
+
if (!hook) {
|
|
111
|
+
throw new Error("plugin.transform is not defined");
|
|
112
|
+
}
|
|
113
|
+
const handler = typeof hook === "function" ? hook : hook.handler;
|
|
114
|
+
return handler.call(
|
|
115
|
+
// biome-ignore lint/suspicious/noExplicitAny: vitest harness for plugin hook
|
|
116
|
+
{}, code, id, { moduleType: "js" });
|
|
117
|
+
}
|
|
118
|
+
it("rewrites asset paths in JS modules", () => {
|
|
119
|
+
const result = runTransform("/src/widget.tsx", `const logo = "/assets/logo.png";`);
|
|
120
|
+
expect(result?.code).toContain(`(window.skybridge?.serverUrl ?? "") + "/assets/logo.png"`);
|
|
121
|
+
});
|
|
122
|
+
// Reproducer for #697: CSS imports are served as JS modules with the
|
|
123
|
+
// stylesheet embedded as a string. Rewriting url("/foo.woff2") inside that
|
|
124
|
+
// string would produce invalid CSS once the styles are injected.
|
|
125
|
+
it("does not rewrite asset paths when transforming a CSS module", () => {
|
|
126
|
+
const cssCode = `__vite__updateStyle("style-id", "@font-face { src: url(\\"/fonts/Brand.woff2\\") format(\\"woff2\\"); }");`;
|
|
127
|
+
expect(runTransform("/src/fonts.css", cssCode)).toBeNull();
|
|
128
|
+
expect(runTransform("/src/fonts.css?direct", cssCode)).toBeNull();
|
|
129
|
+
expect(runTransform("/src/fonts.css?inline", cssCode)).toBeNull();
|
|
130
|
+
expect(runTransform("/src/styles.scss", cssCode)).toBeNull();
|
|
131
|
+
expect(runTransform("/src/Foo.vue?vue&type=style&lang.css", cssCode)).toBeNull();
|
|
132
|
+
});
|
|
55
133
|
});
|
|
56
134
|
//# sourceMappingURL=asset-base-url-transform-plugin.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-base-url-transform-plugin.test.js","sourceRoot":"","sources":["../../src/server/asset-base-url-transform-plugin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAC;AAE7E,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,MAAM,eAAe,GAAG,uBAAuB,CAAC;IAEhD,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,kBAAkB,eAAe,oBAAoB;aAChE;YACD;gBACE,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,kBAAkB,eAAe,oBAAoB;aAChE;YACD;gBACE,IAAI,EAAE,iBAAiB;gBACvB,IAAI,EAAE,mCAAmC;gBACzC,QAAQ,EAAE,mBAAmB,eAAe,qBAAqB;aAClE;SACF,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,KAAK,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YAC5D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG;;;;KAIZ,CAAC;QACF,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,eAAe,kBAAkB,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,eAAe,kBAAkB,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,eAAe,oBAAoB,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG;;;;KAIZ,CAAC;QACF,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,GAAG,eAAe,kBAAkB,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,IAAI,GAAG,6BAA6B,CAAC;QAC3C,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;QAE5D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"asset-base-url-transform-plugin.test.js","sourceRoot":"","sources":["../../src/server/asset-base-url-transform-plugin.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, it } from \"vitest\";\nimport {\n assetBaseUrlTransform,\n assetBaseUrlTransformPlugin,\n isCssRequest,\n} from \"./asset-base-url-transform-plugin.js\";\n\ndescribe(\"assetBaseUrlTransform\", () => {\n it(\"should transform asset paths to use window.skybridge.serverUrl\", () => {\n const cases = [\n {\n desc: \"single-quoted\",\n code: `const image = '/assets/logo.png';`,\n expected: `const image = (window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\";`,\n },\n {\n desc: \"double-quoted\",\n code: `const image = \"/assets/logo.png\";`,\n expected: `const image = (window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\";`,\n },\n {\n desc: \"backtick-quoted\",\n code: \"const image = `/assets/logo.png`;\",\n expected: `const image = (window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\";`,\n },\n ];\n\n for (const { code, expected } of cases) {\n const result = assetBaseUrlTransform(code);\n expect(result).toBe(expected);\n }\n });\n\n it(\"should transform multiple asset paths\", () => {\n const code = `\n const logo = '/assets/logo.png';\n const icon = '/assets/icon.svg';\n const font = '/assets/font.woff2';\n `;\n const result = assetBaseUrlTransform(code);\n\n expect(result).toContain(\n `(window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\"`,\n );\n expect(result).toContain(\n `(window.skybridge?.serverUrl ?? \"\") + \"/assets/icon.svg\"`,\n );\n expect(result).toContain(\n `(window.skybridge?.serverUrl ?? \"\") + \"/assets/font.woff2\"`,\n );\n });\n\n it(\"should not transform already absolute URLs\", () => {\n const code = `\n const local = '/assets/logo.png';\n const http = 'http://example.com/image.png';\n const https = 'https://example.com/image.png';\n `;\n const result = assetBaseUrlTransform(code);\n\n expect(result).toContain(\n `(window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\"`,\n );\n expect(result).toContain(\"http://example.com/image.png\");\n expect(result).toContain(\"https://example.com/image.png\");\n });\n\n it(\"should not transform code without asset paths\", () => {\n const code = `const text = \"Hello World\";`;\n const result = assetBaseUrlTransform(code);\n\n expect(result).toBe(code);\n });\n\n it(\"should not transform asset paths inside static `import ... from` clauses\", () => {\n // Reproducer for #713: a dep does `import * as sprite from './icons.svg'`,\n // Vite resolves the relative path to absolute, then this transform used\n // to rewrite the resolved string — producing invalid JS like\n // `import * as sprite from (expr) + \"...\"` that crashes vite:import-analysis.\n const cases = [\n `import * as sprite from \"/Users/me/proj/node_modules/pkg/icons.svg\";`,\n `import sprite from '/assets/icons.svg';`,\n `import sprite from \"/assets/icons.svg\";`,\n `export { default } from \"/assets/icons.svg\";`,\n `export * from '/assets/sprites.svg';`,\n ];\n\n for (const code of cases) {\n expect(assetBaseUrlTransform(code)).toBe(code);\n }\n });\n\n it(\"should still transform value-position asset paths in files that also have unrelated imports\", () => {\n const code = [\n `import { foo } from \"./foo.js\";`,\n `import * as sprite from \"/assets/sprite.svg\";`,\n `const logo = \"/assets/logo.png\";`,\n ].join(\"\\n\");\n const result = assetBaseUrlTransform(code);\n\n // Imports untouched\n expect(result).toContain(`from \"./foo.js\"`);\n expect(result).toContain(`from \"/assets/sprite.svg\"`);\n // Value-position rewritten\n expect(result).toContain(\n `const logo = (window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\";`,\n );\n });\n});\n\ndescribe(\"isCssRequest\", () => {\n it(\"returns true for CSS-family extensions\", () => {\n expect(isCssRequest(\"/src/styles.css\")).toBe(true);\n expect(isCssRequest(\"/src/styles.module.css\")).toBe(true);\n expect(isCssRequest(\"/src/styles.scss\")).toBe(true);\n expect(isCssRequest(\"/src/styles.sass\")).toBe(true);\n expect(isCssRequest(\"/src/styles.less\")).toBe(true);\n expect(isCssRequest(\"/src/styles.styl\")).toBe(true);\n });\n\n it(\"returns true for CSS modules with Vite query strings\", () => {\n expect(isCssRequest(\"/src/styles.css?direct\")).toBe(true);\n expect(isCssRequest(\"/src/styles.css?inline\")).toBe(true);\n expect(isCssRequest(\"/src/styles.css?used\")).toBe(true);\n expect(isCssRequest(\"/src/Foo.vue?vue&type=style&lang.css\")).toBe(true);\n });\n\n it(\"returns false for non-CSS modules\", () => {\n expect(isCssRequest(\"/src/index.tsx\")).toBe(false);\n expect(isCssRequest(\"/src/utils.ts\")).toBe(false);\n expect(isCssRequest(\"/src/Logo.svg\")).toBe(false);\n expect(isCssRequest(\"/src/notes.cssx\")).toBe(false);\n });\n});\n\ndescribe(\"assetBaseUrlTransformPlugin\", () => {\n type TransformResult = { code: string; map: null } | null;\n\n function runTransform(id: string, code: string): TransformResult {\n const plugin = assetBaseUrlTransformPlugin();\n const hook = plugin.transform;\n if (!hook) {\n throw new Error(\"plugin.transform is not defined\");\n }\n const handler = typeof hook === \"function\" ? hook : hook.handler;\n return handler.call(\n // biome-ignore lint/suspicious/noExplicitAny: vitest harness for plugin hook\n {} as any,\n code,\n id,\n { moduleType: \"js\" },\n ) as TransformResult;\n }\n\n it(\"rewrites asset paths in JS modules\", () => {\n const result = runTransform(\n \"/src/widget.tsx\",\n `const logo = \"/assets/logo.png\";`,\n );\n expect(result?.code).toContain(\n `(window.skybridge?.serverUrl ?? \"\") + \"/assets/logo.png\"`,\n );\n });\n\n // Reproducer for #697: CSS imports are served as JS modules with the\n // stylesheet embedded as a string. Rewriting url(\"/foo.woff2\") inside that\n // string would produce invalid CSS once the styles are injected.\n it(\"does not rewrite asset paths when transforming a CSS module\", () => {\n const cssCode = `__vite__updateStyle(\"style-id\", \"@font-face { src: url(\\\\\"/fonts/Brand.woff2\\\\\") format(\\\\\"woff2\\\\\"); }\");`;\n\n expect(runTransform(\"/src/fonts.css\", cssCode)).toBeNull();\n expect(runTransform(\"/src/fonts.css?direct\", cssCode)).toBeNull();\n expect(runTransform(\"/src/fonts.css?inline\", cssCode)).toBeNull();\n expect(runTransform(\"/src/styles.scss\", cssCode)).toBeNull();\n expect(\n runTransform(\"/src/Foo.vue?vue&type=style&lang.css\", cssCode),\n ).toBeNull();\n });\n});\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Discovery doc validated as OAuth AS metadata + `jwks_uri`. The SDK's
|
|
4
|
+
* `OAuthMetadataSchema` omits `jwks_uri` (its client never verifies tokens), but
|
|
5
|
+
* we need it for JWKS token verification, so we extend the schema to keep it.
|
|
6
|
+
*/
|
|
7
|
+
declare const DiscoverySchema: z.ZodObject<{
|
|
8
|
+
issuer: z.ZodString;
|
|
9
|
+
authorization_endpoint: z.ZodURL;
|
|
10
|
+
token_endpoint: z.ZodURL;
|
|
11
|
+
registration_endpoint: z.ZodOptional<z.ZodURL>;
|
|
12
|
+
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
response_types_supported: z.ZodArray<z.ZodString>;
|
|
14
|
+
response_modes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
|
+
grant_types_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
16
|
+
token_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
|
+
token_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
18
|
+
service_documentation: z.ZodOptional<z.ZodURL>;
|
|
19
|
+
revocation_endpoint: z.ZodOptional<z.ZodURL>;
|
|
20
|
+
revocation_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
21
|
+
revocation_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
22
|
+
introspection_endpoint: z.ZodOptional<z.ZodString>;
|
|
23
|
+
introspection_endpoint_auth_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
24
|
+
introspection_endpoint_auth_signing_alg_values_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
25
|
+
code_challenge_methods_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
client_id_metadata_document_supported: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
jwks_uri: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, z.core.$loose>;
|
|
29
|
+
export type DiscoveredMetadata = z.infer<typeof DiscoverySchema>;
|
|
30
|
+
/** Fetches and validates an authorization server's discovery document. */
|
|
31
|
+
export declare function discoverAuthorizationServer(issuer: string): Promise<DiscoveredMetadata>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { OAuthMetadataSchema } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* Discovery doc validated as OAuth AS metadata + `jwks_uri`. The SDK's
|
|
5
|
+
* `OAuthMetadataSchema` omits `jwks_uri` (its client never verifies tokens), but
|
|
6
|
+
* we need it for JWKS token verification, so we extend the schema to keep it.
|
|
7
|
+
*/
|
|
8
|
+
const DiscoverySchema = OAuthMetadataSchema.extend({
|
|
9
|
+
jwks_uri: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
const WELL_KNOWN = [
|
|
12
|
+
"/.well-known/openid-configuration",
|
|
13
|
+
"/.well-known/oauth-authorization-server",
|
|
14
|
+
];
|
|
15
|
+
const DISCOVERY_TIMEOUT_MS = 5000;
|
|
16
|
+
/** Fetches and validates an authorization server's discovery document. */
|
|
17
|
+
export async function discoverAuthorizationServer(issuer) {
|
|
18
|
+
const base = issuer.replace(/\/$/, "");
|
|
19
|
+
const errors = [];
|
|
20
|
+
// First valid doc lacking a registration_endpoint, kept as a fallback so a
|
|
21
|
+
// genuinely non-DCR IdP still resolves (and is rejected later by the caller).
|
|
22
|
+
let fallback;
|
|
23
|
+
for (const path of WELL_KNOWN) {
|
|
24
|
+
const url = `${base}${path}`;
|
|
25
|
+
// Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer
|
|
26
|
+
// mismatch) records a per-URL reason and falls through to the next path.
|
|
27
|
+
try {
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
throw new Error(`HTTP ${res.status}`);
|
|
33
|
+
}
|
|
34
|
+
const meta = DiscoverySchema.parse(await res.json());
|
|
35
|
+
// RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).
|
|
36
|
+
if (meta.issuer.replace(/\/$/, "") !== base) {
|
|
37
|
+
throw new Error(`issuer mismatch: ${meta.issuer}`);
|
|
38
|
+
}
|
|
39
|
+
// Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a
|
|
40
|
+
// valid openid-configuration without `registration_endpoint` yet expose
|
|
41
|
+
// it at oauth-authorization-server. Keep looking past a doc that omits it.
|
|
42
|
+
if (meta.registration_endpoint) {
|
|
43
|
+
return meta;
|
|
44
|
+
}
|
|
45
|
+
fallback ??= meta;
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
errors.push(`${url}: ${err instanceof Error ? err.message : String(err)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (fallback) {
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join("; ")}`);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,eAAe,GAAG,mBAAmB,CAAC,MAAM,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAChC,CAAC,CAAC;AAGH,MAAM,UAAU,GAAG;IACjB,mCAAmC;IACnC,yCAAyC;CAC1C,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAElC,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,MAAc;IAEd,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,2EAA2E;IAC3E,8EAA8E;IAC9E,IAAI,QAAwC,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;QAC7B,4EAA4E;QAC5E,yEAAyE;QACzE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAC3B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,oBAAoB,CAAC;aAClD,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxC,CAAC;YACD,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YACrD,yEAAyE;YACzE,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC5C,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACrD,CAAC;YACD,0EAA0E;YAC1E,wEAAwE;YACxE,2EAA2E;YAC3E,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAC/B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,QAAQ,KAAK,IAAI,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,GAAG,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,8BAA8B,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChF,CAAC","sourcesContent":["import { OAuthMetadataSchema } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport { z } from \"zod\";\n\n/**\n * Discovery doc validated as OAuth AS metadata + `jwks_uri`. The SDK's\n * `OAuthMetadataSchema` omits `jwks_uri` (its client never verifies tokens), but\n * we need it for JWKS token verification, so we extend the schema to keep it.\n */\nconst DiscoverySchema = OAuthMetadataSchema.extend({\n jwks_uri: z.string().optional(),\n});\nexport type DiscoveredMetadata = z.infer<typeof DiscoverySchema>;\n\nconst WELL_KNOWN = [\n \"/.well-known/openid-configuration\",\n \"/.well-known/oauth-authorization-server\",\n];\n\nconst DISCOVERY_TIMEOUT_MS = 5000;\n\n/** Fetches and validates an authorization server's discovery document. */\nexport async function discoverAuthorizationServer(\n issuer: string,\n): Promise<DiscoveredMetadata> {\n const base = issuer.replace(/\\/$/, \"\");\n const errors: string[] = [];\n // First valid doc lacking a registration_endpoint, kept as a fallback so a\n // genuinely non-DCR IdP still resolves (and is rejected later by the caller).\n let fallback: DiscoveredMetadata | undefined;\n\n for (const path of WELL_KNOWN) {\n const url = `${base}${path}`;\n // Any failure (network/timeout, non-2xx, non-JSON, invalid metadata, issuer\n // mismatch) records a per-URL reason and falls through to the next path.\n try {\n const res = await fetch(url, {\n signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status}`);\n }\n const meta = DiscoverySchema.parse(await res.json());\n // RFC 8414 §3.3: issuer must match the fetch origin (slash-insensitive).\n if (meta.issuer.replace(/\\/$/, \"\") !== base) {\n throw new Error(`issuer mismatch: ${meta.issuer}`);\n }\n // Prefer the document that advertises DCR: some IdPs (e.g. Clerk) serve a\n // valid openid-configuration without `registration_endpoint` yet expose\n // it at oauth-authorization-server. Keep looking past a doc that omits it.\n if (meta.registration_endpoint) {\n return meta;\n }\n fallback ??= meta;\n } catch (err) {\n errors.push(\n `${url}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (fallback) {\n return fallback;\n }\n throw new Error(`OAuth discovery failed for ${issuer}: ${errors.join(\"; \")}`);\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { discoverAuthorizationServer } from "./discovery.js";
|
|
5
|
+
let server;
|
|
6
|
+
afterEach(() => server?.close());
|
|
7
|
+
function doc(origin, extra = {}) {
|
|
8
|
+
return {
|
|
9
|
+
issuer: origin,
|
|
10
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
11
|
+
token_endpoint: `${origin}/token`,
|
|
12
|
+
registration_endpoint: `${origin}/register`,
|
|
13
|
+
response_types_supported: ["code"],
|
|
14
|
+
scopes_supported: ["openid", "email"],
|
|
15
|
+
jwks_uri: `${origin}/jwks`,
|
|
16
|
+
...extra,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
// Serves JSON bodies built from the live origin at their well-known paths;
|
|
20
|
+
// returns the origin URL.
|
|
21
|
+
async function serve(build) {
|
|
22
|
+
let origin = "";
|
|
23
|
+
const srv = http.createServer((req, res) => {
|
|
24
|
+
const body = build(origin)[req.url ?? ""];
|
|
25
|
+
if (body === undefined) {
|
|
26
|
+
res.writeHead(404).end();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
res.setHeader("content-type", "application/json");
|
|
30
|
+
res.end(JSON.stringify(body));
|
|
31
|
+
});
|
|
32
|
+
await new Promise((resolve) => srv.listen(0, resolve));
|
|
33
|
+
server = srv;
|
|
34
|
+
origin = `http://localhost:${srv.address().port}`;
|
|
35
|
+
return origin;
|
|
36
|
+
}
|
|
37
|
+
describe("discoverAuthorizationServer", () => {
|
|
38
|
+
it("fetches and validates openid-configuration", async () => {
|
|
39
|
+
const base = await serve((o) => ({
|
|
40
|
+
"/.well-known/openid-configuration": doc(o),
|
|
41
|
+
}));
|
|
42
|
+
const meta = await discoverAuthorizationServer(base);
|
|
43
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
44
|
+
expect(meta.jwks_uri).toBe(`${base}/jwks`);
|
|
45
|
+
});
|
|
46
|
+
it("accepts a canonically slash-terminated issuer", async () => {
|
|
47
|
+
const base = await serve((o) => ({
|
|
48
|
+
"/.well-known/openid-configuration": doc(o, { issuer: `${o}/` }),
|
|
49
|
+
}));
|
|
50
|
+
const meta = await discoverAuthorizationServer(base);
|
|
51
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
52
|
+
});
|
|
53
|
+
it("falls back to oauth-authorization-server when oidc is 404", async () => {
|
|
54
|
+
const base = await serve((o) => ({
|
|
55
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
56
|
+
}));
|
|
57
|
+
const meta = await discoverAuthorizationServer(base);
|
|
58
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
59
|
+
});
|
|
60
|
+
it("throws when no well-known doc is reachable", async () => {
|
|
61
|
+
const base = await serve(() => ({}));
|
|
62
|
+
await expect(discoverAuthorizationServer(base)).rejects.toThrow(/discovery failed/i);
|
|
63
|
+
});
|
|
64
|
+
it("falls through to oauth-authorization-server when oidc doc is schema-invalid", async () => {
|
|
65
|
+
const base = await serve((o) => ({
|
|
66
|
+
"/.well-known/openid-configuration": doc(o, {
|
|
67
|
+
response_types_supported: undefined,
|
|
68
|
+
}),
|
|
69
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
70
|
+
}));
|
|
71
|
+
const meta = await discoverAuthorizationServer(base);
|
|
72
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
73
|
+
});
|
|
74
|
+
it("prefers the doc advertising DCR when oidc omits registration_endpoint", async () => {
|
|
75
|
+
// Clerk-shaped: valid openid-configuration without a registration_endpoint,
|
|
76
|
+
// DCR exposed only at oauth-authorization-server.
|
|
77
|
+
const base = await serve((o) => ({
|
|
78
|
+
"/.well-known/openid-configuration": doc(o, {
|
|
79
|
+
registration_endpoint: undefined,
|
|
80
|
+
}),
|
|
81
|
+
"/.well-known/oauth-authorization-server": doc(o),
|
|
82
|
+
}));
|
|
83
|
+
const meta = await discoverAuthorizationServer(base);
|
|
84
|
+
expect(meta.registration_endpoint).toBe(`${base}/register`);
|
|
85
|
+
});
|
|
86
|
+
it("rejects a doc whose issuer does not match the fetch origin", async () => {
|
|
87
|
+
const base = await serve(() => ({
|
|
88
|
+
"/.well-known/openid-configuration": doc("https://evil.test"),
|
|
89
|
+
}));
|
|
90
|
+
await expect(discoverAuthorizationServer(base)).rejects.toThrow(/discovery failed/i);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=discovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.test.js","sourceRoot":"","sources":["../../../src/server/auth/discovery.test.ts"],"names":[],"mappings":"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","sourcesContent":["// @vitest-environment node\nimport http from \"node:http\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { discoverAuthorizationServer } from \"./discovery.js\";\n\nlet server: http.Server | undefined;\nafterEach(() => server?.close());\n\nfunction doc(origin: string, extra: Record<string, unknown> = {}) {\n return {\n issuer: origin,\n authorization_endpoint: `${origin}/authorize`,\n token_endpoint: `${origin}/token`,\n registration_endpoint: `${origin}/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"email\"],\n jwks_uri: `${origin}/jwks`,\n ...extra,\n };\n}\n\n// Serves JSON bodies built from the live origin at their well-known paths;\n// returns the origin URL.\nasync function serve(build: (origin: string) => Record<string, unknown>) {\n let origin = \"\";\n const srv = http.createServer((req, res) => {\n const body = build(origin)[req.url ?? \"\"];\n if (body === undefined) {\n res.writeHead(404).end();\n return;\n }\n res.setHeader(\"content-type\", \"application/json\");\n res.end(JSON.stringify(body));\n });\n await new Promise<void>((resolve) => srv.listen(0, resolve));\n server = srv;\n origin = `http://localhost:${(srv.address() as { port: number }).port}`;\n return origin;\n}\n\ndescribe(\"discoverAuthorizationServer\", () => {\n it(\"fetches and validates openid-configuration\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n expect(meta.jwks_uri).toBe(`${base}/jwks`);\n });\n\n it(\"accepts a canonically slash-terminated issuer\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, { issuer: `${o}/` }),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"falls back to oauth-authorization-server when oidc is 404\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"throws when no well-known doc is reachable\", async () => {\n const base = await serve(() => ({}));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n\n it(\"falls through to oauth-authorization-server when oidc doc is schema-invalid\", async () => {\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n response_types_supported: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"prefers the doc advertising DCR when oidc omits registration_endpoint\", async () => {\n // Clerk-shaped: valid openid-configuration without a registration_endpoint,\n // DCR exposed only at oauth-authorization-server.\n const base = await serve((o) => ({\n \"/.well-known/openid-configuration\": doc(o, {\n registration_endpoint: undefined,\n }),\n \"/.well-known/oauth-authorization-server\": doc(o),\n }));\n const meta = await discoverAuthorizationServer(base);\n expect(meta.registration_endpoint).toBe(`${base}/register`);\n });\n\n it(\"rejects a doc whose issuer does not match the fetch origin\", async () => {\n const base = await serve(() => ({\n \"/.well-known/openid-configuration\": doc(\"https://evil.test\"),\n }));\n await expect(discoverAuthorizationServer(base)).rejects.toThrow(\n /discovery failed/i,\n );\n });\n});\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";
|
|
2
|
+
import type { JwksVerifyConfig } from "./verify.js";
|
|
3
|
+
/** Resource-server OAuth config for `SkybridgeServerOptions.oauth`. */
|
|
4
|
+
export type OAuthConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Public URL of this server; sets `resourceServerUrl` and the
|
|
7
|
+
* `resource_metadata` URL. When omitted, it is inferred per request from
|
|
8
|
+
* `x-forwarded-host`/`origin`/`host` headers.
|
|
9
|
+
*/
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
/** AS metadata served at `/.well-known/oauth-authorization-server`. */
|
|
12
|
+
oauthMetadata: OAuthMetadata;
|
|
13
|
+
verify: JwksVerifyConfig;
|
|
14
|
+
/** Scopes advertised in protected-resource metadata. */
|
|
15
|
+
scopesSupported?: string[];
|
|
16
|
+
/** Server-wide required-scope floor. */
|
|
17
|
+
requiredScopes?: string[];
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/auth/index.ts"],"names":[],"mappings":"","sourcesContent":["import type { OAuthMetadata } from \"@modelcontextprotocol/sdk/shared/auth.js\";\nimport type { JwksVerifyConfig } from \"./verify.js\";\n\n/** Resource-server OAuth config for `SkybridgeServerOptions.oauth`. */\nexport type OAuthConfig = {\n /**\n * Public URL of this server; sets `resourceServerUrl` and the\n * `resource_metadata` URL. When omitted, it is inferred per request from\n * `x-forwarded-host`/`origin`/`host` headers.\n */\n baseUrl?: string;\n /** AS metadata served at `/.well-known/oauth-authorization-server`. */\n oauthMetadata: OAuthMetadata;\n verify: JwksVerifyConfig;\n /** Scopes advertised in protected-resource metadata. */\n scopesSupported?: string[];\n /** Server-wide required-scope floor. */\n requiredScopes?: string[];\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Auth0. `domain` is the tenant domain (e.g.
|
|
5
|
+
* `acme.us.auth0.com`); `audience` is the API Identifier; `serverUrl` is this
|
|
6
|
+
* server's public URL. Requires DCR enabled on the tenant.
|
|
7
|
+
*
|
|
8
|
+
* Auth0 can't use the client's resource indicator — `audience` must be *in* the
|
|
9
|
+
* authorize request. So it runs as skybridge-as-AS (`serverUrl`, see
|
|
10
|
+
* {@link customProvider}) and bakes `?audience=<id>` into the advertised
|
|
11
|
+
* `authorization_endpoint`. The token's `aud` is the API; `verify.issuer` stays
|
|
12
|
+
* Auth0 (the token's real `iss`).
|
|
13
|
+
*/
|
|
14
|
+
export declare function auth0Provider(opts: {
|
|
15
|
+
domain: string;
|
|
16
|
+
audience: string;
|
|
17
|
+
serverUrl: string;
|
|
18
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience" | "baseUrl" | "serverUrl">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
import { toIssuerUrl } from "./shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Auth0. `domain` is the tenant domain (e.g.
|
|
5
|
+
* `acme.us.auth0.com`); `audience` is the API Identifier; `serverUrl` is this
|
|
6
|
+
* server's public URL. Requires DCR enabled on the tenant.
|
|
7
|
+
*
|
|
8
|
+
* Auth0 can't use the client's resource indicator — `audience` must be *in* the
|
|
9
|
+
* authorize request. So it runs as skybridge-as-AS (`serverUrl`, see
|
|
10
|
+
* {@link customProvider}) and bakes `?audience=<id>` into the advertised
|
|
11
|
+
* `authorization_endpoint`. The token's `aud` is the API; `verify.issuer` stays
|
|
12
|
+
* Auth0 (the token's real `iss`).
|
|
13
|
+
*/
|
|
14
|
+
export async function auth0Provider(opts) {
|
|
15
|
+
const { domain, audience, ...rest } = opts;
|
|
16
|
+
const config = await customProvider({
|
|
17
|
+
issuer: toIssuerUrl(domain),
|
|
18
|
+
audience,
|
|
19
|
+
...rest,
|
|
20
|
+
});
|
|
21
|
+
const authUrl = new URL(config.oauthMetadata.authorization_endpoint);
|
|
22
|
+
authUrl.searchParams.set("audience", audience);
|
|
23
|
+
return {
|
|
24
|
+
...config,
|
|
25
|
+
oauthMetadata: {
|
|
26
|
+
...config.oauthMetadata,
|
|
27
|
+
authorization_endpoint: authUrl.href,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=auth0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth0.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/auth0.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAGC;IAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;QAClC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;QAC3B,QAAQ;QACR,GAAG,IAAI;KACR,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;IACrE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,MAAM;QACT,aAAa,EAAE;YACb,GAAG,MAAM,CAAC,aAAa;YACvB,sBAAsB,EAAE,OAAO,CAAC,IAAI;SACrC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\nimport { toIssuerUrl } from \"./shared.js\";\n\n/**\n * OAuth provider for Auth0. `domain` is the tenant domain (e.g.\n * `acme.us.auth0.com`); `audience` is the API Identifier; `serverUrl` is this\n * server's public URL. Requires DCR enabled on the tenant.\n *\n * Auth0 can't use the client's resource indicator — `audience` must be *in* the\n * authorize request. So it runs as skybridge-as-AS (`serverUrl`, see\n * {@link customProvider}) and bakes `?audience=<id>` into the advertised\n * `authorization_endpoint`. The token's `aud` is the API; `verify.issuer` stays\n * Auth0 (the token's real `iss`).\n */\nexport async function auth0Provider(\n opts: { domain: string; audience: string; serverUrl: string } & Omit<\n CustomProviderOptions,\n \"issuer\" | \"audience\" | \"baseUrl\" | \"serverUrl\"\n >,\n): Promise<OAuthConfig> {\n const { domain, audience, ...rest } = opts;\n const config = await customProvider({\n issuer: toIssuerUrl(domain),\n audience,\n ...rest,\n });\n const authUrl = new URL(config.oauthMetadata.authorization_endpoint);\n authUrl.searchParams.set(\"audience\", audience);\n return {\n ...config,\n oauthMetadata: {\n ...config.oauthMetadata,\n authorization_endpoint: authUrl.href,\n },\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { auth0Provider } from "./auth0.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
function doc(issuer) {
|
|
6
|
+
return {
|
|
7
|
+
issuer,
|
|
8
|
+
authorization_endpoint: `${issuer}/authorize`,
|
|
9
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
10
|
+
registration_endpoint: `${issuer}/oidc/register`,
|
|
11
|
+
response_types_supported: ["code"],
|
|
12
|
+
scopes_supported: ["openid", "profile", "email"],
|
|
13
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe("auth0Provider", () => {
|
|
17
|
+
it("advertises skybridge as the AS (static issuer) and bakes audience; verifies against Auth0", async () => {
|
|
18
|
+
const issuer = "https://acme.us.auth0.com";
|
|
19
|
+
vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(doc(issuer)), {
|
|
20
|
+
headers: { "content-type": "application/json" },
|
|
21
|
+
}));
|
|
22
|
+
const config = await auth0Provider({
|
|
23
|
+
domain: "acme.us.auth0.com",
|
|
24
|
+
audience: "https://api.example.com/",
|
|
25
|
+
serverUrl: "https://app.example.com/",
|
|
26
|
+
scopes: ["openid", "profile", "email"],
|
|
27
|
+
});
|
|
28
|
+
// skybridge is the advertised AS: static issuer = serverUrl (trailing slash stripped).
|
|
29
|
+
expect(config.oauthMetadata.issuer).toBe("https://app.example.com");
|
|
30
|
+
// AS metadata scopes match the advertised set (clients read scopes from here).
|
|
31
|
+
expect(config.oauthMetadata.scopes_supported).toEqual([
|
|
32
|
+
"openid",
|
|
33
|
+
"profile",
|
|
34
|
+
"email",
|
|
35
|
+
]);
|
|
36
|
+
expect(config.scopesSupported).toEqual(["openid", "profile", "email"]);
|
|
37
|
+
// audience baked into the advertised authorize endpoint (still pointing at Auth0).
|
|
38
|
+
const url = new URL(config.oauthMetadata.authorization_endpoint);
|
|
39
|
+
expect(url.origin + url.pathname).toBe(`${issuer}/authorize`);
|
|
40
|
+
expect(url.searchParams.get("audience")).toBe("https://api.example.com/");
|
|
41
|
+
// baseUrl left unset so the PRM resource resolves per request (Alpic-safe).
|
|
42
|
+
expect(config.baseUrl).toBeUndefined();
|
|
43
|
+
// token verification still uses Auth0 as issuer + the API audience.
|
|
44
|
+
expect(config.verify.issuer).toBe(issuer);
|
|
45
|
+
expect(config.verify.audience).toBe("https://api.example.com/");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=auth0.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth0.test.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/auth0.test.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;AAEtC,SAAS,GAAG,CAAC,MAAc;IACzB,OAAO;QACL,MAAM;QACN,sBAAsB,EAAE,GAAG,MAAM,YAAY;QAC7C,cAAc,EAAE,GAAG,MAAM,cAAc;QACvC,qBAAqB,EAAE,GAAG,MAAM,gBAAgB;QAChD,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC;QAChD,QAAQ,EAAE,GAAG,MAAM,wBAAwB;KAC5C,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;QACzG,MAAM,MAAM,GAAG,2BAA2B,CAAC;QAC3C,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,iBAAiB,CAC7C,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE;YACxC,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;SAChD,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,MAAM,EAAE,mBAAmB;YAC3B,QAAQ,EAAE,0BAA0B;YACpC,SAAS,EAAE,0BAA0B;YACrC,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC;SACvC,CAAC,CAAC;QAEH,uFAAuF;QACvF,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QACpE,+EAA+E;QAC/E,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC;YACpD,QAAQ;YACR,SAAS;YACT,OAAO;SACR,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;QACvE,mFAAmF;QACnF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,sBAAsB,CAAC,CAAC;QACjE,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC1E,4EAA4E;QAC5E,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;QACvC,oEAAoE;QACpE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// @vitest-environment node\nimport { afterEach, describe, expect, it, vi } from \"vitest\";\nimport { auth0Provider } from \"./auth0.js\";\n\nafterEach(() => vi.restoreAllMocks());\n\nfunction doc(issuer: string) {\n return {\n issuer,\n authorization_endpoint: `${issuer}/authorize`,\n token_endpoint: `${issuer}/oauth/token`,\n registration_endpoint: `${issuer}/oidc/register`,\n response_types_supported: [\"code\"],\n scopes_supported: [\"openid\", \"profile\", \"email\"],\n jwks_uri: `${issuer}/.well-known/jwks.json`,\n };\n}\n\ndescribe(\"auth0Provider\", () => {\n it(\"advertises skybridge as the AS (static issuer) and bakes audience; verifies against Auth0\", async () => {\n const issuer = \"https://acme.us.auth0.com\";\n vi.spyOn(globalThis, \"fetch\").mockResolvedValue(\n new Response(JSON.stringify(doc(issuer)), {\n headers: { \"content-type\": \"application/json\" },\n }),\n );\n\n const config = await auth0Provider({\n domain: \"acme.us.auth0.com\",\n audience: \"https://api.example.com/\",\n serverUrl: \"https://app.example.com/\",\n scopes: [\"openid\", \"profile\", \"email\"],\n });\n\n // skybridge is the advertised AS: static issuer = serverUrl (trailing slash stripped).\n expect(config.oauthMetadata.issuer).toBe(\"https://app.example.com\");\n // AS metadata scopes match the advertised set (clients read scopes from here).\n expect(config.oauthMetadata.scopes_supported).toEqual([\n \"openid\",\n \"profile\",\n \"email\",\n ]);\n expect(config.scopesSupported).toEqual([\"openid\", \"profile\", \"email\"]);\n // audience baked into the advertised authorize endpoint (still pointing at Auth0).\n const url = new URL(config.oauthMetadata.authorization_endpoint);\n expect(url.origin + url.pathname).toBe(`${issuer}/authorize`);\n expect(url.searchParams.get(\"audience\")).toBe(\"https://api.example.com/\");\n // baseUrl left unset so the PRM resource resolves per request (Alpic-safe).\n expect(config.baseUrl).toBeUndefined();\n // token verification still uses Auth0 as issuer + the API audience.\n expect(config.verify.issuer).toBe(issuer);\n expect(config.verify.audience).toBe(\"https://api.example.com/\");\n });\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OAuthConfig } from "../index.js";
|
|
2
|
+
import { type CustomProviderOptions } from "./custom.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
5
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
6
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
7
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
8
|
+
*
|
|
9
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
10
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
11
|
+
*/
|
|
12
|
+
export declare function clerkProvider(opts: {
|
|
13
|
+
domain: string;
|
|
14
|
+
} & Omit<CustomProviderOptions, "issuer" | "audience">): Promise<OAuthConfig>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { customProvider } from "./custom.js";
|
|
2
|
+
import { toIssuerUrl } from "./shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.
|
|
5
|
+
* `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic
|
|
6
|
+
* Client Registration enabled on the instance, and the OAuth application set to
|
|
7
|
+
* issue JWT access tokens (opaque tokens can't be JWKS-verified).
|
|
8
|
+
*
|
|
9
|
+
* Clerk access tokens carry no `aud` claim, so there is no `audience` option —
|
|
10
|
+
* verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).
|
|
11
|
+
*/
|
|
12
|
+
export function clerkProvider(opts) {
|
|
13
|
+
const { domain, ...rest } = opts;
|
|
14
|
+
return customProvider({ issuer: toIssuerUrl(domain), ...rest });
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=clerk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clerk.js","sourceRoot":"","sources":["../../../../src/server/auth/providers/clerk.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,cAAc,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,IAA6E;IAE7E,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjC,OAAO,cAAc,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { OAuthConfig } from \"../index.js\";\nimport { type CustomProviderOptions, customProvider } from \"./custom.js\";\nimport { toIssuerUrl } from \"./shared.js\";\n\n/**\n * OAuth provider for Clerk. `domain` is the Frontend API URL (e.g.\n * `acme.clerk.accounts.dev`, or a production custom domain). Requires Dynamic\n * Client Registration enabled on the instance, and the OAuth application set to\n * issue JWT access tokens (opaque tokens can't be JWKS-verified).\n *\n * Clerk access tokens carry no `aud` claim, so there is no `audience` option —\n * verification is issuer + JWKS only (matching Clerk's own `mcpAuthClerk`).\n */\nexport function clerkProvider(\n opts: { domain: string } & Omit<CustomProviderOptions, \"issuer\" | \"audience\">,\n): Promise<OAuthConfig> {\n const { domain, ...rest } = opts;\n return customProvider({ issuer: toIssuerUrl(domain), ...rest });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { clerkProvider } from "./clerk.js";
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
function doc(issuer) {
|
|
6
|
+
return {
|
|
7
|
+
issuer,
|
|
8
|
+
authorization_endpoint: `${issuer}/oauth/authorize`,
|
|
9
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
10
|
+
registration_endpoint: `${issuer}/oauth/register`,
|
|
11
|
+
response_types_supported: ["code"],
|
|
12
|
+
scopes_supported: ["profile", "email"],
|
|
13
|
+
jwks_uri: `${issuer}/.well-known/jwks.json`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
describe("clerkProvider", () => {
|
|
17
|
+
it("verifies issuer + JWKS with no audience (Clerk tokens carry no aud)", async () => {
|
|
18
|
+
const issuer = "https://acme.clerk.accounts.dev";
|
|
19
|
+
const fetchSpy = vi.spyOn(globalThis, "fetch").mockResolvedValue(new Response(JSON.stringify(doc(issuer)), {
|
|
20
|
+
headers: { "content-type": "application/json" },
|
|
21
|
+
}));
|
|
22
|
+
const config = await clerkProvider({ domain: "acme.clerk.accounts.dev" });
|
|
23
|
+
expect(fetchSpy).toHaveBeenCalledWith(`${issuer}/.well-known/openid-configuration`, expect.anything());
|
|
24
|
+
expect(config.verify.issuer).toBe(issuer);
|
|
25
|
+
expect(config.verify.audience).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=clerk.test.js.map
|