@lukoweb/apitogo 0.1.23 → 0.1.37
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 +92 -0
- package/cli.js +13 -13
- package/client.d.ts +8 -8
- package/dist/cli/cli.js +1117 -624
- package/dist/declarations/config/config.d.ts +4 -0
- package/dist/declarations/config/loader.d.ts +2 -0
- package/dist/declarations/config/resolve-modules.d.ts +34 -0
- package/dist/declarations/config/validators/InputNavigationSchema.d.ts +7 -7
- package/dist/declarations/config/validators/ModulesSchema.d.ts +417 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +123 -4
- package/dist/declarations/index.d.ts +3 -1
- package/dist/declarations/lib/authentication/components/ProductionUnlockPage.d.ts +1 -0
- package/dist/declarations/lib/authentication/hook.d.ts +3 -19
- package/dist/declarations/lib/authentication/providers/dev-portal-constants.d.ts +16 -0
- package/dist/declarations/lib/authentication/providers/dev-portal-utils.d.ts +9 -0
- package/dist/declarations/lib/authentication/providers/dev-portal.d.ts +39 -0
- package/dist/declarations/lib/authentication/state.d.ts +12 -26
- package/dist/declarations/lib/components/LandingLayout.d.ts +4 -0
- package/dist/declarations/lib/components/index.d.ts +2 -0
- package/dist/declarations/lib/plugins/api-keys/index.d.ts +2 -1
- package/dist/flat-config.d.ts +209 -24
- package/docs/components/alert.mdx +130 -0
- package/docs/components/badge.mdx +70 -0
- package/docs/components/button.mdx +132 -0
- package/docs/components/callout.mdx +112 -0
- package/docs/components/card.mdx +104 -0
- package/docs/components/checkbox.mdx +72 -0
- package/docs/components/client-only.mdx +79 -0
- package/docs/components/code-tabs.mdx +179 -0
- package/docs/components/dialog.mdx +167 -0
- package/docs/components/head.mdx +200 -0
- package/docs/components/icons.mdx +27 -0
- package/docs/components/input.mdx +96 -0
- package/docs/components/label.mdx +86 -0
- package/docs/components/link.mdx +242 -0
- package/docs/components/markdown.mdx +151 -0
- package/docs/components/mermaid.mdx +81 -0
- package/docs/components/playground.mdx +87 -0
- package/docs/components/secret.mdx +79 -0
- package/docs/components/select.mdx +176 -0
- package/docs/components/shadcn.mdx +73 -0
- package/docs/components/slider.mdx +108 -0
- package/docs/components/slot.mdx +119 -0
- package/docs/components/stepper.mdx +138 -0
- package/docs/components/switch.mdx +96 -0
- package/docs/components/syntax-highlight.mdx +602 -0
- package/docs/components/textarea.mdx +78 -0
- package/docs/components/tooltip.mdx +195 -0
- package/docs/components/typography.mdx +61 -0
- package/docs/concepts/auth-provider-api-identities.md +109 -0
- package/docs/configuration/ai-assistants.md +65 -0
- package/docs/configuration/api-catalog.md +109 -0
- package/docs/configuration/api-reference.md +397 -0
- package/docs/configuration/authentication-auth0.md +174 -0
- package/docs/configuration/authentication-azure-ad.md +238 -0
- package/docs/configuration/authentication-clerk.md +110 -0
- package/docs/configuration/authentication-firebase.md +62 -0
- package/docs/configuration/authentication-pingfederate.md +136 -0
- package/docs/configuration/authentication-supabase.md +226 -0
- package/docs/configuration/authentication.md +231 -0
- package/docs/configuration/build-configuration.mdx +147 -0
- package/docs/configuration/docs.md +282 -0
- package/docs/configuration/footer.mdx +214 -0
- package/docs/configuration/llms.md +90 -0
- package/docs/configuration/make-config.mdx +90 -0
- package/docs/configuration/navigation.mdx +422 -0
- package/docs/configuration/overview.md +380 -0
- package/docs/configuration/protected-routes.md +150 -0
- package/docs/configuration/search.md +163 -0
- package/docs/configuration/sentry.mdx +44 -0
- package/docs/configuration/site.md +124 -0
- package/docs/configuration/slots.mdx +125 -0
- package/docs/configuration/vite-config.md +18 -0
- package/docs/custom-plugins.md +285 -0
- package/docs/customization/colors-theme.mdx +276 -0
- package/docs/customization/fonts.md +110 -0
- package/docs/deploy/apache-nginx.md +41 -0
- package/docs/deploy/apitogo.md +139 -0
- package/docs/deploy/cloudflare-pages.md +75 -0
- package/docs/deploy/direct-upload.md +18 -0
- package/docs/deploy/github-pages.md +50 -0
- package/docs/deploy/vercel.md +103 -0
- package/docs/deployment.md +21 -0
- package/docs/extending/events.md +124 -0
- package/docs/guides/custom-pages.md +106 -0
- package/docs/guides/environment-variables.md +99 -0
- package/docs/guides/managing-api-keys-and-identities.md +172 -0
- package/docs/guides/mermaid.mdx +70 -0
- package/docs/guides/navigation-migration.md +87 -0
- package/docs/guides/navigation-rules.mdx +203 -0
- package/docs/guides/processors.mdx +234 -0
- package/docs/guides/static-files.md +55 -0
- package/docs/guides/transforming-examples.md +156 -0
- package/docs/guides/using-multiple-apis.md +87 -0
- package/docs/markdown/admonitions.md +128 -0
- package/docs/markdown/code-blocks.md +196 -0
- package/docs/markdown/frontmatter.md +173 -0
- package/docs/markdown/mdx.md +68 -0
- package/docs/markdown/overview.md +275 -0
- package/docs/plugins.md +5 -0
- package/docs/quickstart.md +87 -0
- package/docs/writing.mdx +72 -0
- package/package.json +170 -116
- package/src/app/ZuploBuildConfig.ts +36 -36
- package/src/app/defaultTheme.css +77 -77
- package/src/app/demo-cdn.html +37 -37
- package/src/app/demo.html +21 -21
- package/src/app/demo.tsx +77 -77
- package/src/app/entry.client.tsx +118 -118
- package/src/app/entry.server.tsx +160 -160
- package/src/app/env.ts +40 -40
- package/src/app/font.geist.css +73 -73
- package/src/app/main.css +390 -390
- package/src/app/main.tsx +8 -1
- package/src/app/polyfills.ts +47 -47
- package/src/app/processRoutes.tsx +51 -25
- package/src/app/sentry.ts +24 -24
- package/src/app/standalone.html +23 -23
- package/src/app/standalone.tsx +60 -60
- package/src/app/utils/createRedirectRoutes.ts +11 -11
- package/src/config/config.ts +32 -0
- package/src/config/create-plugin.ts +71 -71
- package/src/config/file-exists.ts +6 -6
- package/src/config/loader.ts +11 -1
- package/src/config/resolve-modules.ts +260 -0
- package/src/config/validators/BuildSchema.ts +95 -95
- package/src/config/validators/HeaderNavigationSchema.ts +41 -41
- package/src/config/validators/InputNavigationSchema.test-d.ts +178 -178
- package/src/config/validators/InputNavigationSchema.ts +206 -206
- package/src/config/validators/ModulesSchema.ts +312 -0
- package/src/config/validators/ProtectedRoutesSchema.ts +35 -35
- package/src/config/validators/ZudokuConfig.ts +766 -729
- package/src/config/validators/auth.ts +3 -3
- package/src/config/validators/reason-codes.ts +7 -7
- package/src/env.d.ts +11 -11
- package/src/index.ts +31 -0
- package/src/lib/MissingIcon.tsx +22 -22
- package/src/lib/assets/language-icons/bun.tsx +50 -50
- package/src/lib/assets/language-icons/c.tsx +31 -31
- package/src/lib/assets/language-icons/commonlisp.tsx +22 -22
- package/src/lib/assets/language-icons/cpp.tsx +35 -35
- package/src/lib/assets/language-icons/csharp.tsx +35 -35
- package/src/lib/assets/language-icons/css.tsx +36 -36
- package/src/lib/assets/language-icons/dart.tsx +39 -39
- package/src/lib/assets/language-icons/elixir.tsx +19 -19
- package/src/lib/assets/language-icons/go.tsx +19 -19
- package/src/lib/assets/language-icons/graphql.tsx +19 -19
- package/src/lib/assets/language-icons/html.tsx +24 -24
- package/src/lib/assets/language-icons/java.tsx +35 -35
- package/src/lib/assets/language-icons/javascript.tsx +11 -11
- package/src/lib/assets/language-icons/json.tsx +19 -19
- package/src/lib/assets/language-icons/kotlin.tsx +30 -30
- package/src/lib/assets/language-icons/markdown.tsx +19 -19
- package/src/lib/assets/language-icons/mdx.tsx +23 -23
- package/src/lib/assets/language-icons/npm.tsx +15 -15
- package/src/lib/assets/language-icons/objectivec.tsx +23 -23
- package/src/lib/assets/language-icons/ocaml.tsx +34 -34
- package/src/lib/assets/language-icons/php.tsx +19 -19
- package/src/lib/assets/language-icons/pnpm.tsx +32 -32
- package/src/lib/assets/language-icons/powershell.tsx +27 -27
- package/src/lib/assets/language-icons/python.tsx +23 -23
- package/src/lib/assets/language-icons/react.tsx +21 -21
- package/src/lib/assets/language-icons/ruby.tsx +19 -19
- package/src/lib/assets/language-icons/rust.tsx +19 -19
- package/src/lib/assets/language-icons/scala.tsx +19 -19
- package/src/lib/assets/language-icons/shell.tsx +19 -19
- package/src/lib/assets/language-icons/swift.tsx +19 -19
- package/src/lib/assets/language-icons/toml.tsx +23 -23
- package/src/lib/assets/language-icons/typescript.tsx +23 -23
- package/src/lib/assets/language-icons/xml.tsx +19 -19
- package/src/lib/assets/language-icons/yaml.tsx +23 -23
- package/src/lib/assets/language-icons/yarn.tsx +17 -17
- package/src/lib/assets/language-icons/zig.tsx +32 -32
- package/src/lib/auth/issuer.ts +34 -31
- package/src/lib/authentication/AuthenticationPlugin.tsx +33 -33
- package/src/lib/authentication/authentication.ts +48 -48
- package/src/lib/authentication/components/OAuthErrorPage.tsx +197 -197
- package/src/lib/authentication/components/ProductionUnlockPage.tsx +27 -0
- package/src/lib/authentication/components/SignOut.tsx +15 -15
- package/src/lib/authentication/constants.ts +1 -1
- package/src/lib/authentication/errors.ts +34 -34
- package/src/lib/authentication/hook.ts +141 -139
- package/src/lib/authentication/providers/auth0.tsx +91 -91
- package/src/lib/authentication/providers/azureb2c.tsx +223 -223
- package/src/lib/authentication/providers/dev-portal-constants.ts +21 -0
- package/src/lib/authentication/providers/dev-portal-utils.ts +107 -0
- package/src/lib/authentication/providers/dev-portal.tsx +270 -0
- package/src/lib/authentication/providers/firebase.tsx +486 -486
- package/src/lib/authentication/providers/openid.tsx +516 -516
- package/src/lib/authentication/providers/supabase.tsx +412 -412
- package/src/lib/authentication/providers/util.ts +26 -26
- package/src/lib/authentication/state.ts +179 -91
- package/src/lib/authentication/ui/icons/Apple.tsx +10 -10
- package/src/lib/authentication/ui/icons/Facebook.tsx +15 -15
- package/src/lib/authentication/ui/icons/Github.tsx +16 -16
- package/src/lib/authentication/ui/icons/Google.tsx +16 -16
- package/src/lib/authentication/ui/icons/Microsoft.tsx +12 -12
- package/src/lib/authentication/ui/icons/X.tsx +10 -10
- package/src/lib/authentication/utils/relativeRedirectUrl.ts +16 -16
- package/src/lib/components/AnchorLink.tsx +24 -24
- package/src/lib/components/Banner.tsx +52 -52
- package/src/lib/components/Bootstrap.tsx +77 -77
- package/src/lib/components/BuildCheck.tsx +87 -87
- package/src/lib/components/CategoryHeading.tsx +19 -19
- package/src/lib/components/ClientOnly.tsx +19 -19
- package/src/lib/components/ErrorPage.tsx +26 -26
- package/src/lib/components/Footer.tsx +140 -140
- package/src/lib/components/Framed.tsx +51 -51
- package/src/lib/components/Header.tsx +11 -3
- package/src/lib/components/HeaderNavigation.tsx +148 -148
- package/src/lib/components/Heading.tsx +84 -84
- package/src/lib/components/InlineCode.tsx +15 -15
- package/src/lib/components/LandingLayout.tsx +21 -0
- package/src/lib/components/LanguageIcon.tsx +199 -199
- package/src/lib/components/Markdown.tsx +55 -55
- package/src/lib/components/Meta.tsx +46 -46
- package/src/lib/components/MobileTopNavigation.tsx +11 -3
- package/src/lib/components/NotFoundPage.tsx +37 -37
- package/src/lib/components/PageProgress.tsx +28 -28
- package/src/lib/components/PagefindSearchMeta.tsx +14 -14
- package/src/lib/components/Pagination.tsx +44 -44
- package/src/lib/components/PathRenderer.tsx +61 -61
- package/src/lib/components/PluginHeads.tsx +17 -17
- package/src/lib/components/Search.tsx +70 -70
- package/src/lib/components/Slot.tsx +64 -64
- package/src/lib/components/Spinner.tsx +5 -5
- package/src/lib/components/StatusPage.tsx +96 -96
- package/src/lib/components/ThemeSwitch.tsx +49 -49
- package/src/lib/components/Typography.tsx +12 -12
- package/src/lib/components/Zudoku.tsx +106 -106
- package/src/lib/components/cache.ts +27 -27
- package/src/lib/components/context/RenderContext.ts +11 -11
- package/src/lib/components/context/RouterEventsEmitter.tsx +19 -19
- package/src/lib/components/context/SlotProvider.tsx +149 -149
- package/src/lib/components/context/ViewportAnchorContext.tsx +137 -137
- package/src/lib/components/context/ZudokuContext.ts +140 -140
- package/src/lib/components/context/ZudokuProvider.tsx +22 -22
- package/src/lib/components/context/ZudokuReactContext.tsx +17 -17
- package/src/lib/components/index.ts +2 -0
- package/src/lib/components/navigation/Navigation.tsx +51 -51
- package/src/lib/components/navigation/NavigationBadge.tsx +48 -48
- package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -28
- package/src/lib/components/navigation/NavigationWrapper.tsx +49 -49
- package/src/lib/components/navigation/Toc.tsx +137 -137
- package/src/lib/components/navigation/ZudokuLogo.tsx +25 -25
- package/src/lib/components/navigation/ZuploLogo.tsx +14 -14
- package/src/lib/components/navigation/utils.ts +234 -234
- package/src/lib/core/RouteGuard.tsx +11 -1
- package/src/lib/core/ZudokuContext.ts +271 -271
- package/src/lib/core/plugins.ts +138 -138
- package/src/lib/core/react-query.ts +1 -1
- package/src/lib/core/router.ts +1 -1
- package/src/lib/core/transform-config.ts +67 -67
- package/src/lib/demo/DemoAnnouncement.tsx +17 -17
- package/src/lib/errors/ErrorAlert.tsx +28 -28
- package/src/lib/errors/RouterError.tsx +18 -18
- package/src/lib/errors/ServerError.tsx +5 -5
- package/src/lib/errors/TopLevelError.tsx +6 -6
- package/src/lib/hooks/index.ts +13 -13
- package/src/lib/hooks/useEvent.ts +41 -41
- package/src/lib/hooks/useHighlighter.ts +15 -15
- package/src/lib/hooks/useHotkey.ts +70 -70
- package/src/lib/icons.ts +2 -2
- package/src/lib/navigation/applyRules.ts +127 -127
- package/src/lib/navigation/pathMatcher.ts +81 -81
- package/src/lib/oas/graphql/circular.ts +69 -69
- package/src/lib/oas/graphql/constants.ts +2 -2
- package/src/lib/oas/graphql/index.ts +791 -791
- package/src/lib/oas/parser/dereference/index.ts +84 -84
- package/src/lib/oas/parser/dereference/resolveRef.ts +32 -32
- package/src/lib/oas/parser/index.ts +129 -129
- package/src/lib/oas/parser/schemas/v3.0.json +1489 -1489
- package/src/lib/oas/parser/schemas/v3.1.json +1298 -1298
- package/src/lib/oas/parser/upgrade/index.ts +174 -174
- package/src/lib/plugins/api-catalog/index.tsx +119 -119
- package/src/lib/plugins/api-keys/ProtectedRoute.tsx +33 -33
- package/src/lib/plugins/api-keys/index.tsx +274 -272
- package/src/lib/plugins/api-keys/settings/ApiKeyList.tsx +67 -67
- package/src/lib/plugins/custom-pages/index.tsx +32 -32
- package/src/lib/plugins/markdown/assets/ChatGPTLogo.tsx +11 -11
- package/src/lib/plugins/markdown/assets/ClaudeLogo.tsx +19 -19
- package/src/lib/plugins/markdown/index.tsx +62 -62
- package/src/lib/plugins/openapi/ColorizedParam.tsx +115 -115
- package/src/lib/plugins/openapi/Endpoint.tsx +85 -85
- package/src/lib/plugins/openapi/MCPEndpoint.tsx +273 -273
- package/src/lib/plugins/openapi/OasProvider.tsx +74 -74
- package/src/lib/plugins/openapi/OperationList.tsx +275 -275
- package/src/lib/plugins/openapi/ParamInfos.tsx +96 -96
- package/src/lib/plugins/openapi/ParameterList.tsx +53 -53
- package/src/lib/plugins/openapi/ParameterListItem.tsx +161 -161
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +67 -67
- package/src/lib/plugins/openapi/SidecarBox.tsx +57 -57
- package/src/lib/plugins/openapi/SimpleSelect.tsx +46 -46
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +73 -73
- package/src/lib/plugins/openapi/client/GraphQLContext.tsx +16 -16
- package/src/lib/plugins/openapi/client/createServer.ts +35 -35
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +44 -44
- package/src/lib/plugins/openapi/components/ConstValue.tsx +24 -24
- package/src/lib/plugins/openapi/components/NonHighlightedCode.tsx +22 -22
- package/src/lib/plugins/openapi/components/SelectOnClick.tsx +29 -29
- package/src/lib/plugins/openapi/context.tsx +16 -16
- package/src/lib/plugins/openapi/graphql/fragment-masking.ts +104 -104
- package/src/lib/plugins/openapi/graphql/gql.ts +95 -95
- package/src/lib/plugins/openapi/graphql/graphql.ts +835 -835
- package/src/lib/plugins/openapi/graphql/index.ts +2 -2
- package/src/lib/plugins/openapi/index.tsx +221 -221
- package/src/lib/plugins/openapi/interfaces.ts +100 -100
- package/src/lib/plugins/openapi/playground/Headers.tsx +6 -6
- package/src/lib/plugins/openapi/playground/PathParams.tsx +53 -53
- package/src/lib/plugins/openapi/playground/Spinner.tsx +87 -87
- package/src/lib/plugins/openapi/playground/createUrl.ts +25 -25
- package/src/lib/plugins/openapi/playground/fileUtils.ts +36 -36
- package/src/lib/plugins/openapi/playground/rememberedIdentity.ts +26 -26
- package/src/lib/plugins/openapi/playground/request-panel/UrlPath.tsx +31 -31
- package/src/lib/plugins/openapi/playground/request-panel/UrlQueryParams.tsx +29 -29
- package/src/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.ts +340 -340
- package/src/lib/plugins/openapi/playground/result-panel/ResponseStatusBar.tsx +117 -117
- package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.ts +36 -36
- package/src/lib/plugins/openapi/playground/serializeQueryParams.ts +125 -125
- package/src/lib/plugins/openapi/playground/useRememberSkipLoginDialog.tsx +23 -23
- package/src/lib/plugins/openapi/processors/removeExtensions.ts +29 -29
- package/src/lib/plugins/openapi/processors/removeParameters.ts +103 -103
- package/src/lib/plugins/openapi/processors/removePaths.ts +57 -57
- package/src/lib/plugins/openapi/processors/traverse.ts +1 -1
- package/src/lib/plugins/openapi/schema/SchemaExampleAndDefault.tsx +39 -39
- package/src/lib/plugins/openapi/schema/UnionView.tsx +132 -132
- package/src/lib/plugins/openapi/schema/union-helpers.ts +123 -123
- package/src/lib/plugins/openapi/schema/utils.ts +49 -49
- package/src/lib/plugins/openapi/state.ts +36 -36
- package/src/lib/plugins/openapi/util/buildTagCategories.ts +54 -54
- package/src/lib/plugins/openapi/util/createHttpSnippet.ts +135 -135
- package/src/lib/plugins/openapi/util/createNavigationCategory.tsx +39 -39
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +191 -191
- package/src/lib/plugins/openapi/util/getRoutes.tsx +265 -265
- package/src/lib/plugins/openapi/util/methodColorMap.tsx +11 -11
- package/src/lib/plugins/openapi/util/methodToColor.ts +27 -27
- package/src/lib/plugins/openapi/util/sanitizeMarkdownForMetatag.tsx +32 -32
- package/src/lib/plugins/openapi/util/useWarmupSchema.ts +23 -23
- package/src/lib/plugins/search-inkeep/index.tsx +108 -108
- package/src/lib/plugins/search-inkeep/inkeep.ts +24 -24
- package/src/lib/plugins/search-pagefind/get-results.tsx +74 -74
- package/src/lib/plugins/search-pagefind/index.tsx +21 -21
- package/src/lib/plugins/search-pagefind/types.ts +118 -118
- package/src/lib/shiki-constants.ts +24 -24
- package/src/lib/testing/index.tsx +146 -146
- package/src/lib/ui/Accordion.tsx +55 -55
- package/src/lib/ui/Alert.tsx +84 -84
- package/src/lib/ui/AlertDialog.tsx +195 -195
- package/src/lib/ui/AspectRatio.tsx +5 -5
- package/src/lib/ui/Badge.tsx +48 -48
- package/src/lib/ui/Breadcrumb.tsx +115 -115
- package/src/lib/ui/Button.tsx +65 -65
- package/src/lib/ui/ButtonGroup.tsx +80 -80
- package/src/lib/ui/Callout.tsx +113 -113
- package/src/lib/ui/Card.tsx +95 -95
- package/src/lib/ui/Carousel.tsx +259 -259
- package/src/lib/ui/Checkbox.tsx +28 -28
- package/src/lib/ui/CodeBlock.tsx +92 -92
- package/src/lib/ui/CodeTabPanel.tsx +13 -13
- package/src/lib/ui/CodeTabs.tsx +161 -161
- package/src/lib/ui/Collapsible.tsx +31 -31
- package/src/lib/ui/Dialog.tsx +138 -138
- package/src/lib/ui/DismissibleAlert.tsx +74 -74
- package/src/lib/ui/Drawer.tsx +121 -121
- package/src/lib/ui/DropdownMenu.tsx +254 -254
- package/src/lib/ui/EmbeddedCodeBlock.tsx +99 -99
- package/src/lib/ui/Form.tsx +176 -176
- package/src/lib/ui/Frame.tsx +81 -81
- package/src/lib/ui/HoverCard.tsx +26 -26
- package/src/lib/ui/Input.tsx +23 -23
- package/src/lib/ui/InputGroup.tsx +168 -168
- package/src/lib/ui/Item.tsx +213 -213
- package/src/lib/ui/Kbd.tsx +28 -28
- package/src/lib/ui/Label.tsx +23 -23
- package/src/lib/ui/NativeSelect.tsx +48 -48
- package/src/lib/ui/NavigationMenu.tsx +169 -169
- package/src/lib/ui/Pagination.tsx +115 -115
- package/src/lib/ui/Popover.tsx +28 -28
- package/src/lib/ui/Progress.tsx +25 -25
- package/src/lib/ui/RadioGroup.tsx +41 -41
- package/src/lib/ui/ReactComponentDoc.tsx +68 -68
- package/src/lib/ui/ScrollArea.tsx +45 -45
- package/src/lib/ui/Secret.tsx +133 -133
- package/src/lib/ui/Select.tsx +184 -184
- package/src/lib/ui/Separator.tsx +25 -25
- package/src/lib/ui/Skeleton.tsx +15 -15
- package/src/lib/ui/Slider.tsx +25 -25
- package/src/lib/ui/Stepper.tsx +8 -8
- package/src/lib/ui/Switch.tsx +26 -26
- package/src/lib/ui/SyntaxHighlight.tsx +54 -54
- package/src/lib/ui/Tabs.tsx +52 -52
- package/src/lib/ui/Textarea.tsx +22 -22
- package/src/lib/ui/Toggle.tsx +42 -42
- package/src/lib/ui/ToggleGroup.tsx +58 -58
- package/src/lib/ui/Tooltip.tsx +65 -65
- package/src/lib/ui/Value.tsx +42 -42
- package/src/lib/ui/util.tsx +8 -8
- package/src/lib/util/cn.ts +6 -6
- package/src/lib/util/createVariantComponent.tsx +61 -61
- package/src/lib/util/ensureArray.ts +3 -3
- package/src/lib/util/flattenAllOf.ts +72 -72
- package/src/lib/util/flattenAllOfProcessor.ts +74 -74
- package/src/lib/util/hastToJsx.tsx +67 -67
- package/src/lib/util/humanFileSize.ts +15 -15
- package/src/lib/util/invariant.ts +52 -52
- package/src/lib/util/joinUrl.ts +60 -60
- package/src/lib/util/logInit.ts +9 -9
- package/src/lib/util/objectEntries.ts +5 -5
- package/src/lib/util/os.ts +18 -18
- package/src/lib/util/pastellize.ts +25 -25
- package/src/lib/util/problemJson.ts +63 -63
- package/src/lib/util/readFrontmatter.ts +14 -14
- package/src/lib/util/renderIf.ts +4 -4
- package/src/lib/util/scrollIntoViewIfNeeded.ts +18 -18
- package/src/lib/util/slugify.ts +21 -21
- package/src/lib/util/syncZustandState.ts +22 -22
- package/src/lib/util/traverse.ts +70 -70
- package/src/lib/util/types.ts +7 -7
- package/src/lib/util/url.ts +18 -18
- package/src/lib/util/useCopyToClipboard.ts +17 -17
- package/src/lib/util/useExposedProps.tsx +27 -27
- package/src/lib/util/useIsomorphicLayoutEffect.ts +6 -6
- package/src/lib/util/useLatest.ts +18 -18
- package/src/lib/util/useOnScreen.ts +34 -34
- package/src/lib/util/useScrollToAnchor.ts +70 -70
- package/src/lib/util/useScrollToTop.ts +18 -18
- package/src/types.d.ts +57 -53
- package/src/vite/api/SchemaManager.ts +371 -371
- package/src/vite/api/schema-codegen.ts +163 -163
- package/src/vite/config.ts +5 -1
- package/src/vite/css/collect.ts +42 -42
- package/src/vite/css/plugin.ts +105 -105
- package/src/vite/debug.ts +18 -18
- package/src/vite/dev-portal-env.ts +93 -0
- package/src/vite/html.ts +54 -54
- package/src/vite/index.ts +2 -2
- package/src/vite/mdx/rehype-extract-toc-with-jsx-export.ts +19 -19
- package/src/vite/mdx/rehype-extract-toc-with-jsx.ts +70 -70
- package/src/vite/mdx/remark-code-tabs.ts +47 -47
- package/src/vite/mdx/remark-inject-filepath.ts +13 -13
- package/src/vite/mdx/remark-last-modified.ts +103 -103
- package/src/vite/mdx/remark-link-rewrite.ts +23 -23
- package/src/vite/mdx/remark-normalize-image-url.ts +15 -15
- package/src/vite/mdx/remark-static-generation.ts +149 -149
- package/src/vite/mdx/utils.ts +32 -32
- package/src/vite/output.ts +261 -261
- package/src/vite/package-root.ts +12 -12
- package/src/vite/pagefind-dev-index.ts +100 -100
- package/src/vite/plugin-api-keys.ts +6 -1
- package/src/vite/plugin-api.ts +4 -1
- package/src/vite/plugin-auth.ts +5 -1
- package/src/vite/plugin-component.ts +1 -1
- package/src/vite/plugin-config-reload.ts +49 -49
- package/src/vite/plugin-doc-metadata.ts +45 -45
- package/src/vite/plugin-docs.ts +4 -1
- package/src/vite/plugin-metadata.ts +30 -30
- package/src/vite/plugin-modules.ts +144 -0
- package/src/vite/plugin-shiki-register.ts +97 -97
- package/src/vite/plugin-theme.ts +399 -399
- package/src/vite/plugin.ts +2 -0
- package/src/vite/prerender/utils.ts +76 -76
- package/src/vite/prerender/worker.ts +118 -118
- package/src/vite/reporter.ts +34 -34
- package/src/vite/shadcn-registry.ts +48 -48
- package/src/vite/sitemap.ts +78 -78
- package/src/vite/ssr-templates/cloudflare.ts +19 -19
- package/src/vite/ssr-templates/node.ts +25 -25
- package/src/vite/ssr-templates/vercel.ts +22 -22
- package/src/vite/zuplo.ts +30 -30
- package/src/vite-env.d.ts +6 -6
- package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -168
- package/src/zuplo/enrich-with-zuplo.ts +256 -256
- package/src/zuplo/policy-types.ts +46 -46
- package/src/zuplo/with-zuplo-processors.ts +36 -36
- package/src/zuplo/with-zuplo.ts +14 -14
- package/LICENSE.md +0 -18
- package/dist/declarations/lib/components/navigation/ZudokuLogo.d.ts +0 -6
|
@@ -1,412 +1,412 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createClient,
|
|
3
|
-
type Provider,
|
|
4
|
-
type Session,
|
|
5
|
-
type SupabaseClient,
|
|
6
|
-
} from "@supabase/supabase-js";
|
|
7
|
-
import type { SupabaseAuthenticationConfig } from "../../../config/config.js";
|
|
8
|
-
import { ZudokuError } from "../../util/invariant.js";
|
|
9
|
-
import { joinUrl } from "../../util/joinUrl.js";
|
|
10
|
-
import type {
|
|
11
|
-
AuthActionContext,
|
|
12
|
-
AuthActionOptions,
|
|
13
|
-
AuthenticationPlugin,
|
|
14
|
-
AuthenticationProviderInitializer,
|
|
15
|
-
} from "../authentication.js";
|
|
16
|
-
import { CoreAuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
17
|
-
import { SignOut } from "../components/SignOut.js";
|
|
18
|
-
import { AuthorizationError } from "../errors.js";
|
|
19
|
-
import { type UserProfile, useAuthState } from "../state.js";
|
|
20
|
-
import { EmailVerificationUi } from "../ui/EmailVerificationUi.js";
|
|
21
|
-
import {
|
|
22
|
-
ZudokuPasswordResetUi,
|
|
23
|
-
ZudokuPasswordUpdateUi,
|
|
24
|
-
ZudokuSignInUi,
|
|
25
|
-
ZudokuSignUpUi,
|
|
26
|
-
} from "../ui/ZudokuAuthUi.js";
|
|
27
|
-
|
|
28
|
-
export type SupabaseProviderData = {
|
|
29
|
-
type: "supabase";
|
|
30
|
-
session: Session;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
declare module "../state.js" {
|
|
34
|
-
interface ProviderDataRegistry {
|
|
35
|
-
supabase: SupabaseProviderData;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
class SupabaseAuthenticationProvider
|
|
40
|
-
extends CoreAuthenticationPlugin
|
|
41
|
-
implements AuthenticationPlugin
|
|
42
|
-
{
|
|
43
|
-
private readonly client: SupabaseClient;
|
|
44
|
-
private readonly config: SupabaseAuthenticationConfig;
|
|
45
|
-
private readonly providers: string[];
|
|
46
|
-
private readonly enableUsernamePassword: boolean;
|
|
47
|
-
|
|
48
|
-
constructor(config: SupabaseAuthenticationConfig) {
|
|
49
|
-
const { supabaseUrl, supabaseKey } = config;
|
|
50
|
-
super();
|
|
51
|
-
|
|
52
|
-
this.client = createClient(supabaseUrl, supabaseKey, {
|
|
53
|
-
auth: {
|
|
54
|
-
autoRefreshToken: true,
|
|
55
|
-
persistSession: true,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
this.config = config;
|
|
59
|
-
|
|
60
|
-
// Support both 'provider' (deprecated) and 'providers' config
|
|
61
|
-
const configuredProviders = config.provider
|
|
62
|
-
? [config.provider]
|
|
63
|
-
: (config.providers ?? []);
|
|
64
|
-
this.providers = configuredProviders;
|
|
65
|
-
this.enableUsernamePassword = !config.onlyThirdPartyProviders;
|
|
66
|
-
|
|
67
|
-
this.client.auth.onAuthStateChange(async (event, session) => {
|
|
68
|
-
if (session && (event === "SIGNED_IN" || event === "TOKEN_REFRESHED")) {
|
|
69
|
-
await this.updateUserState(session);
|
|
70
|
-
} else if (event === "SIGNED_OUT") {
|
|
71
|
-
useAuthState.getState().setLoggedOut();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private async updateUserState(session: Session) {
|
|
77
|
-
const { user } = session;
|
|
78
|
-
|
|
79
|
-
const profile: UserProfile = {
|
|
80
|
-
sub: user.id,
|
|
81
|
-
email: user.email,
|
|
82
|
-
name: user.user_metadata.full_name || user.user_metadata.name,
|
|
83
|
-
emailVerified: user.email_confirmed_at != null,
|
|
84
|
-
pictureUrl: user.user_metadata.avatar_url,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
useAuthState.getState().setLoggedIn({
|
|
88
|
-
profile,
|
|
89
|
-
providerData: { type: "supabase", session },
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async getAccessToken(): Promise<string> {
|
|
94
|
-
const { data, error } = await this.client.auth.getSession();
|
|
95
|
-
|
|
96
|
-
if (error || !data.session) {
|
|
97
|
-
throw new AuthorizationError("User is not authenticated");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return data.session.access_token;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async signRequest(request: Request): Promise<Request> {
|
|
104
|
-
const accessToken = await this.getAccessToken();
|
|
105
|
-
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
106
|
-
return request;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
signUp = async (
|
|
110
|
-
{ navigate }: AuthActionContext,
|
|
111
|
-
{ redirectTo }: AuthActionOptions,
|
|
112
|
-
) => {
|
|
113
|
-
void navigate(
|
|
114
|
-
redirectTo
|
|
115
|
-
? `/signup?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
116
|
-
: `/signup`,
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
signIn = async (
|
|
121
|
-
{ navigate }: AuthActionContext,
|
|
122
|
-
{ redirectTo }: AuthActionOptions,
|
|
123
|
-
) => {
|
|
124
|
-
void navigate(
|
|
125
|
-
redirectTo
|
|
126
|
-
? `/signin?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
127
|
-
: `/signin`,
|
|
128
|
-
);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
requestEmailVerification = async (
|
|
132
|
-
{ navigate }: AuthActionContext,
|
|
133
|
-
{ redirectTo }: AuthActionOptions,
|
|
134
|
-
) => {
|
|
135
|
-
const {
|
|
136
|
-
data: { user },
|
|
137
|
-
} = await this.client.auth.getUser();
|
|
138
|
-
if (!user || !user.email) {
|
|
139
|
-
throw new ZudokuError("User is not authenticated", {
|
|
140
|
-
title: "User not authenticated",
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const { error } = await this.client.auth.resend({
|
|
145
|
-
type: "signup",
|
|
146
|
-
email: user.email,
|
|
147
|
-
});
|
|
148
|
-
if (error) {
|
|
149
|
-
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
void navigate(
|
|
153
|
-
redirectTo
|
|
154
|
-
? `/verify-email?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
155
|
-
: `/verify-email`,
|
|
156
|
-
);
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
private onUsernamePasswordSignIn = async (
|
|
160
|
-
email: string,
|
|
161
|
-
password: string,
|
|
162
|
-
) => {
|
|
163
|
-
useAuthState.setState({ isPending: true });
|
|
164
|
-
const { error } = await this.client.auth.signInWithPassword({
|
|
165
|
-
email,
|
|
166
|
-
password,
|
|
167
|
-
});
|
|
168
|
-
useAuthState.setState({ isPending: false });
|
|
169
|
-
if (error) {
|
|
170
|
-
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
private onUsernamePasswordSignUp = async (
|
|
175
|
-
email: string,
|
|
176
|
-
password: string,
|
|
177
|
-
) => {
|
|
178
|
-
useAuthState.setState({ isPending: true });
|
|
179
|
-
const { data, error } = await this.client.auth.signUp({
|
|
180
|
-
email,
|
|
181
|
-
password,
|
|
182
|
-
options: {
|
|
183
|
-
emailRedirectTo: joinUrl(
|
|
184
|
-
window.location.origin,
|
|
185
|
-
this.config.basePath,
|
|
186
|
-
"/verify-email",
|
|
187
|
-
),
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
useAuthState.setState({ isPending: false });
|
|
191
|
-
if (error) {
|
|
192
|
-
throw new Error(getSupabaseErrorMessage(error), { cause: error });
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// If user exists and is confirmed, update state
|
|
196
|
-
if (data.user && data.session) {
|
|
197
|
-
const profile: UserProfile = {
|
|
198
|
-
sub: data.user.id,
|
|
199
|
-
email: data.user.email,
|
|
200
|
-
name: data.user.user_metadata.full_name || data.user.user_metadata.name,
|
|
201
|
-
emailVerified: data.user.email_confirmed_at != null,
|
|
202
|
-
pictureUrl: data.user.user_metadata.avatar_url,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
useAuthState.getState().setLoggedIn({
|
|
206
|
-
profile,
|
|
207
|
-
providerData: { type: "supabase", session: data.session },
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
private onOAuthSignIn = async (providerId: string) => {
|
|
213
|
-
useAuthState.setState({ isPending: true });
|
|
214
|
-
const { error } = await this.client.auth.signInWithOAuth({
|
|
215
|
-
provider: providerId as Provider,
|
|
216
|
-
options: {
|
|
217
|
-
redirectTo:
|
|
218
|
-
this.config.redirectToAfterSignIn ??
|
|
219
|
-
joinUrl(window.location.origin, this.config.basePath),
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
if (error) {
|
|
223
|
-
useAuthState.setState({ isPending: false });
|
|
224
|
-
throw new AuthorizationError(error.message);
|
|
225
|
-
}
|
|
226
|
-
// Note: OAuth sign-in redirects the page, so isPending stays true
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
private onPasswordReset = async (email: string) => {
|
|
230
|
-
const { error } = await this.client.auth.resetPasswordForEmail(email, {
|
|
231
|
-
redirectTo: joinUrl(
|
|
232
|
-
window.location.origin,
|
|
233
|
-
this.config.basePath,
|
|
234
|
-
"/update-password",
|
|
235
|
-
),
|
|
236
|
-
});
|
|
237
|
-
if (error) {
|
|
238
|
-
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
private onPasswordUpdate = async (password: string) => {
|
|
243
|
-
const { error } = await this.client.auth.updateUser({ password });
|
|
244
|
-
if (error) {
|
|
245
|
-
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
private onResendVerification = async () => {
|
|
250
|
-
const {
|
|
251
|
-
data: { user },
|
|
252
|
-
} = await this.client.auth.getUser();
|
|
253
|
-
if (!user || !user.email) {
|
|
254
|
-
throw new ZudokuError("User is not authenticated", {
|
|
255
|
-
title: "User not authenticated",
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
const { error } = await this.client.auth.resend({
|
|
259
|
-
type: "signup",
|
|
260
|
-
email: user.email,
|
|
261
|
-
});
|
|
262
|
-
if (error) {
|
|
263
|
-
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
private onCheckVerification = async (): Promise<boolean> => {
|
|
268
|
-
const { data, error } = await this.client.auth.getUser();
|
|
269
|
-
if (error || !data.user) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const isVerified = data.user.email_confirmed_at != null;
|
|
274
|
-
|
|
275
|
-
if (isVerified) {
|
|
276
|
-
// Refresh the session to get updated token with verified email
|
|
277
|
-
await this.client.auth.refreshSession();
|
|
278
|
-
const { data: sessionData } = await this.client.auth.getSession();
|
|
279
|
-
if (sessionData.session) {
|
|
280
|
-
await this.updateUserState(sessionData.session);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return isVerified;
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
getRoutes = () => {
|
|
288
|
-
return [
|
|
289
|
-
{
|
|
290
|
-
path: "/verify-email",
|
|
291
|
-
element: (
|
|
292
|
-
<EmailVerificationUi
|
|
293
|
-
onResendVerification={this.onResendVerification}
|
|
294
|
-
onCheckVerification={this.onCheckVerification}
|
|
295
|
-
/>
|
|
296
|
-
),
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
path: "/reset-password",
|
|
300
|
-
element: (
|
|
301
|
-
<ZudokuPasswordResetUi onPasswordReset={this.onPasswordReset} />
|
|
302
|
-
),
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
path: "/update-password",
|
|
306
|
-
element: (
|
|
307
|
-
<ZudokuPasswordUpdateUi onPasswordUpdate={this.onPasswordUpdate} />
|
|
308
|
-
),
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
path: "/signin",
|
|
312
|
-
element: (
|
|
313
|
-
<ZudokuSignInUi
|
|
314
|
-
providers={this.providers}
|
|
315
|
-
enableUsernamePassword={this.enableUsernamePassword}
|
|
316
|
-
onOAuthSignIn={this.onOAuthSignIn}
|
|
317
|
-
onUsernamePasswordSignIn={this.onUsernamePasswordSignIn}
|
|
318
|
-
/>
|
|
319
|
-
),
|
|
320
|
-
},
|
|
321
|
-
{
|
|
322
|
-
path: "/signup",
|
|
323
|
-
element: (
|
|
324
|
-
<ZudokuSignUpUi
|
|
325
|
-
providers={this.providers}
|
|
326
|
-
enableUsernamePassword={this.enableUsernamePassword}
|
|
327
|
-
onOAuthSignUp={this.onOAuthSignIn}
|
|
328
|
-
onUsernamePasswordSignUp={this.onUsernamePasswordSignUp}
|
|
329
|
-
/>
|
|
330
|
-
),
|
|
331
|
-
},
|
|
332
|
-
{
|
|
333
|
-
path: "/signout",
|
|
334
|
-
element: <SignOut />,
|
|
335
|
-
},
|
|
336
|
-
];
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
signOut = async () => {
|
|
340
|
-
const { error } = await this.client.auth.signOut({ scope: "local" });
|
|
341
|
-
if (error) {
|
|
342
|
-
// biome-ignore lint/suspicious: Logging is better than not doing anything
|
|
343
|
-
console.error("Error signing out", error);
|
|
344
|
-
}
|
|
345
|
-
useAuthState.getState().setLoggedOut();
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
onPageLoad = async () => {
|
|
349
|
-
const { data, error } = await this.client.auth.getSession();
|
|
350
|
-
|
|
351
|
-
if (!error && data.session) {
|
|
352
|
-
await this.updateUserState(data.session);
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
const supabaseAuth: AuthenticationProviderInitializer<
|
|
358
|
-
SupabaseAuthenticationConfig
|
|
359
|
-
> = (options) => new SupabaseAuthenticationProvider(options);
|
|
360
|
-
|
|
361
|
-
export default supabaseAuth;
|
|
362
|
-
|
|
363
|
-
const getSupabaseErrorMessage = (error: unknown): string => {
|
|
364
|
-
if (!(error instanceof Error)) {
|
|
365
|
-
return "An unexpected error occurred. Please try again.";
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const errorMessage = error.message;
|
|
369
|
-
|
|
370
|
-
// Map common Supabase error messages to user-friendly messages
|
|
371
|
-
if (errorMessage.includes("Invalid login credentials")) {
|
|
372
|
-
return "The email and password you entered don't match.";
|
|
373
|
-
}
|
|
374
|
-
if (errorMessage.includes("Email not confirmed")) {
|
|
375
|
-
return "Please verify your email address before signing in.";
|
|
376
|
-
}
|
|
377
|
-
if (errorMessage.includes("User already registered")) {
|
|
378
|
-
return "The email address is already used by another account.";
|
|
379
|
-
}
|
|
380
|
-
if (
|
|
381
|
-
errorMessage.includes("Password should be at least") ||
|
|
382
|
-
errorMessage.includes("Password must be at least")
|
|
383
|
-
) {
|
|
384
|
-
return "The password must be at least 6 characters long.";
|
|
385
|
-
}
|
|
386
|
-
if (errorMessage.includes("Invalid email")) {
|
|
387
|
-
return "That email address isn't correct.";
|
|
388
|
-
}
|
|
389
|
-
if (errorMessage.includes("Email rate limit exceeded")) {
|
|
390
|
-
return "Too many requests. Please wait a moment and try again.";
|
|
391
|
-
}
|
|
392
|
-
if (errorMessage.includes("For security purposes")) {
|
|
393
|
-
return "For security purposes, please wait a moment before trying again.";
|
|
394
|
-
}
|
|
395
|
-
if (errorMessage.includes("Unable to validate email address")) {
|
|
396
|
-
return "Unable to validate email address. Please check and try again.";
|
|
397
|
-
}
|
|
398
|
-
if (errorMessage.includes("Signups not allowed")) {
|
|
399
|
-
return "Sign ups are not allowed at this time.";
|
|
400
|
-
}
|
|
401
|
-
if (errorMessage.includes("User not found")) {
|
|
402
|
-
return "That email address doesn't match an existing account.";
|
|
403
|
-
}
|
|
404
|
-
if (errorMessage.includes("New password should be different")) {
|
|
405
|
-
return "Your new password must be different from your current password.";
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Return the original message if no mapping found
|
|
409
|
-
return (
|
|
410
|
-
errorMessage || "An error occurred during authentication. Please try again."
|
|
411
|
-
);
|
|
412
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
createClient,
|
|
3
|
+
type Provider,
|
|
4
|
+
type Session,
|
|
5
|
+
type SupabaseClient,
|
|
6
|
+
} from "@supabase/supabase-js";
|
|
7
|
+
import type { SupabaseAuthenticationConfig } from "../../../config/config.js";
|
|
8
|
+
import { ZudokuError } from "../../util/invariant.js";
|
|
9
|
+
import { joinUrl } from "../../util/joinUrl.js";
|
|
10
|
+
import type {
|
|
11
|
+
AuthActionContext,
|
|
12
|
+
AuthActionOptions,
|
|
13
|
+
AuthenticationPlugin,
|
|
14
|
+
AuthenticationProviderInitializer,
|
|
15
|
+
} from "../authentication.js";
|
|
16
|
+
import { CoreAuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
17
|
+
import { SignOut } from "../components/SignOut.js";
|
|
18
|
+
import { AuthorizationError } from "../errors.js";
|
|
19
|
+
import { type UserProfile, useAuthState } from "../state.js";
|
|
20
|
+
import { EmailVerificationUi } from "../ui/EmailVerificationUi.js";
|
|
21
|
+
import {
|
|
22
|
+
ZudokuPasswordResetUi,
|
|
23
|
+
ZudokuPasswordUpdateUi,
|
|
24
|
+
ZudokuSignInUi,
|
|
25
|
+
ZudokuSignUpUi,
|
|
26
|
+
} from "../ui/ZudokuAuthUi.js";
|
|
27
|
+
|
|
28
|
+
export type SupabaseProviderData = {
|
|
29
|
+
type: "supabase";
|
|
30
|
+
session: Session;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
declare module "../state.js" {
|
|
34
|
+
interface ProviderDataRegistry {
|
|
35
|
+
supabase: SupabaseProviderData;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class SupabaseAuthenticationProvider
|
|
40
|
+
extends CoreAuthenticationPlugin
|
|
41
|
+
implements AuthenticationPlugin
|
|
42
|
+
{
|
|
43
|
+
private readonly client: SupabaseClient;
|
|
44
|
+
private readonly config: SupabaseAuthenticationConfig;
|
|
45
|
+
private readonly providers: string[];
|
|
46
|
+
private readonly enableUsernamePassword: boolean;
|
|
47
|
+
|
|
48
|
+
constructor(config: SupabaseAuthenticationConfig) {
|
|
49
|
+
const { supabaseUrl, supabaseKey } = config;
|
|
50
|
+
super();
|
|
51
|
+
|
|
52
|
+
this.client = createClient(supabaseUrl, supabaseKey, {
|
|
53
|
+
auth: {
|
|
54
|
+
autoRefreshToken: true,
|
|
55
|
+
persistSession: true,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
this.config = config;
|
|
59
|
+
|
|
60
|
+
// Support both 'provider' (deprecated) and 'providers' config
|
|
61
|
+
const configuredProviders = config.provider
|
|
62
|
+
? [config.provider]
|
|
63
|
+
: (config.providers ?? []);
|
|
64
|
+
this.providers = configuredProviders;
|
|
65
|
+
this.enableUsernamePassword = !config.onlyThirdPartyProviders;
|
|
66
|
+
|
|
67
|
+
this.client.auth.onAuthStateChange(async (event, session) => {
|
|
68
|
+
if (session && (event === "SIGNED_IN" || event === "TOKEN_REFRESHED")) {
|
|
69
|
+
await this.updateUserState(session);
|
|
70
|
+
} else if (event === "SIGNED_OUT") {
|
|
71
|
+
useAuthState.getState().setLoggedOut();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private async updateUserState(session: Session) {
|
|
77
|
+
const { user } = session;
|
|
78
|
+
|
|
79
|
+
const profile: UserProfile = {
|
|
80
|
+
sub: user.id,
|
|
81
|
+
email: user.email,
|
|
82
|
+
name: user.user_metadata.full_name || user.user_metadata.name,
|
|
83
|
+
emailVerified: user.email_confirmed_at != null,
|
|
84
|
+
pictureUrl: user.user_metadata.avatar_url,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
useAuthState.getState().setLoggedIn({
|
|
88
|
+
profile,
|
|
89
|
+
providerData: { type: "supabase", session },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getAccessToken(): Promise<string> {
|
|
94
|
+
const { data, error } = await this.client.auth.getSession();
|
|
95
|
+
|
|
96
|
+
if (error || !data.session) {
|
|
97
|
+
throw new AuthorizationError("User is not authenticated");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return data.session.access_token;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async signRequest(request: Request): Promise<Request> {
|
|
104
|
+
const accessToken = await this.getAccessToken();
|
|
105
|
+
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
106
|
+
return request;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
signUp = async (
|
|
110
|
+
{ navigate }: AuthActionContext,
|
|
111
|
+
{ redirectTo }: AuthActionOptions,
|
|
112
|
+
) => {
|
|
113
|
+
void navigate(
|
|
114
|
+
redirectTo
|
|
115
|
+
? `/signup?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
116
|
+
: `/signup`,
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
signIn = async (
|
|
121
|
+
{ navigate }: AuthActionContext,
|
|
122
|
+
{ redirectTo }: AuthActionOptions,
|
|
123
|
+
) => {
|
|
124
|
+
void navigate(
|
|
125
|
+
redirectTo
|
|
126
|
+
? `/signin?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
127
|
+
: `/signin`,
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
requestEmailVerification = async (
|
|
132
|
+
{ navigate }: AuthActionContext,
|
|
133
|
+
{ redirectTo }: AuthActionOptions,
|
|
134
|
+
) => {
|
|
135
|
+
const {
|
|
136
|
+
data: { user },
|
|
137
|
+
} = await this.client.auth.getUser();
|
|
138
|
+
if (!user || !user.email) {
|
|
139
|
+
throw new ZudokuError("User is not authenticated", {
|
|
140
|
+
title: "User not authenticated",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const { error } = await this.client.auth.resend({
|
|
145
|
+
type: "signup",
|
|
146
|
+
email: user.email,
|
|
147
|
+
});
|
|
148
|
+
if (error) {
|
|
149
|
+
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
void navigate(
|
|
153
|
+
redirectTo
|
|
154
|
+
? `/verify-email?redirectTo=${encodeURIComponent(redirectTo)}`
|
|
155
|
+
: `/verify-email`,
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
private onUsernamePasswordSignIn = async (
|
|
160
|
+
email: string,
|
|
161
|
+
password: string,
|
|
162
|
+
) => {
|
|
163
|
+
useAuthState.setState({ isPending: true });
|
|
164
|
+
const { error } = await this.client.auth.signInWithPassword({
|
|
165
|
+
email,
|
|
166
|
+
password,
|
|
167
|
+
});
|
|
168
|
+
useAuthState.setState({ isPending: false });
|
|
169
|
+
if (error) {
|
|
170
|
+
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
private onUsernamePasswordSignUp = async (
|
|
175
|
+
email: string,
|
|
176
|
+
password: string,
|
|
177
|
+
) => {
|
|
178
|
+
useAuthState.setState({ isPending: true });
|
|
179
|
+
const { data, error } = await this.client.auth.signUp({
|
|
180
|
+
email,
|
|
181
|
+
password,
|
|
182
|
+
options: {
|
|
183
|
+
emailRedirectTo: joinUrl(
|
|
184
|
+
window.location.origin,
|
|
185
|
+
this.config.basePath,
|
|
186
|
+
"/verify-email",
|
|
187
|
+
),
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
useAuthState.setState({ isPending: false });
|
|
191
|
+
if (error) {
|
|
192
|
+
throw new Error(getSupabaseErrorMessage(error), { cause: error });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// If user exists and is confirmed, update state
|
|
196
|
+
if (data.user && data.session) {
|
|
197
|
+
const profile: UserProfile = {
|
|
198
|
+
sub: data.user.id,
|
|
199
|
+
email: data.user.email,
|
|
200
|
+
name: data.user.user_metadata.full_name || data.user.user_metadata.name,
|
|
201
|
+
emailVerified: data.user.email_confirmed_at != null,
|
|
202
|
+
pictureUrl: data.user.user_metadata.avatar_url,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
useAuthState.getState().setLoggedIn({
|
|
206
|
+
profile,
|
|
207
|
+
providerData: { type: "supabase", session: data.session },
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
private onOAuthSignIn = async (providerId: string) => {
|
|
213
|
+
useAuthState.setState({ isPending: true });
|
|
214
|
+
const { error } = await this.client.auth.signInWithOAuth({
|
|
215
|
+
provider: providerId as Provider,
|
|
216
|
+
options: {
|
|
217
|
+
redirectTo:
|
|
218
|
+
this.config.redirectToAfterSignIn ??
|
|
219
|
+
joinUrl(window.location.origin, this.config.basePath),
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
if (error) {
|
|
223
|
+
useAuthState.setState({ isPending: false });
|
|
224
|
+
throw new AuthorizationError(error.message);
|
|
225
|
+
}
|
|
226
|
+
// Note: OAuth sign-in redirects the page, so isPending stays true
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
private onPasswordReset = async (email: string) => {
|
|
230
|
+
const { error } = await this.client.auth.resetPasswordForEmail(email, {
|
|
231
|
+
redirectTo: joinUrl(
|
|
232
|
+
window.location.origin,
|
|
233
|
+
this.config.basePath,
|
|
234
|
+
"/update-password",
|
|
235
|
+
),
|
|
236
|
+
});
|
|
237
|
+
if (error) {
|
|
238
|
+
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
private onPasswordUpdate = async (password: string) => {
|
|
243
|
+
const { error } = await this.client.auth.updateUser({ password });
|
|
244
|
+
if (error) {
|
|
245
|
+
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
private onResendVerification = async () => {
|
|
250
|
+
const {
|
|
251
|
+
data: { user },
|
|
252
|
+
} = await this.client.auth.getUser();
|
|
253
|
+
if (!user || !user.email) {
|
|
254
|
+
throw new ZudokuError("User is not authenticated", {
|
|
255
|
+
title: "User not authenticated",
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const { error } = await this.client.auth.resend({
|
|
259
|
+
type: "signup",
|
|
260
|
+
email: user.email,
|
|
261
|
+
});
|
|
262
|
+
if (error) {
|
|
263
|
+
throw Error(getSupabaseErrorMessage(error), { cause: error });
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
private onCheckVerification = async (): Promise<boolean> => {
|
|
268
|
+
const { data, error } = await this.client.auth.getUser();
|
|
269
|
+
if (error || !data.user) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const isVerified = data.user.email_confirmed_at != null;
|
|
274
|
+
|
|
275
|
+
if (isVerified) {
|
|
276
|
+
// Refresh the session to get updated token with verified email
|
|
277
|
+
await this.client.auth.refreshSession();
|
|
278
|
+
const { data: sessionData } = await this.client.auth.getSession();
|
|
279
|
+
if (sessionData.session) {
|
|
280
|
+
await this.updateUserState(sessionData.session);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return isVerified;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
getRoutes = () => {
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
path: "/verify-email",
|
|
291
|
+
element: (
|
|
292
|
+
<EmailVerificationUi
|
|
293
|
+
onResendVerification={this.onResendVerification}
|
|
294
|
+
onCheckVerification={this.onCheckVerification}
|
|
295
|
+
/>
|
|
296
|
+
),
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
path: "/reset-password",
|
|
300
|
+
element: (
|
|
301
|
+
<ZudokuPasswordResetUi onPasswordReset={this.onPasswordReset} />
|
|
302
|
+
),
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
path: "/update-password",
|
|
306
|
+
element: (
|
|
307
|
+
<ZudokuPasswordUpdateUi onPasswordUpdate={this.onPasswordUpdate} />
|
|
308
|
+
),
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
path: "/signin",
|
|
312
|
+
element: (
|
|
313
|
+
<ZudokuSignInUi
|
|
314
|
+
providers={this.providers}
|
|
315
|
+
enableUsernamePassword={this.enableUsernamePassword}
|
|
316
|
+
onOAuthSignIn={this.onOAuthSignIn}
|
|
317
|
+
onUsernamePasswordSignIn={this.onUsernamePasswordSignIn}
|
|
318
|
+
/>
|
|
319
|
+
),
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
path: "/signup",
|
|
323
|
+
element: (
|
|
324
|
+
<ZudokuSignUpUi
|
|
325
|
+
providers={this.providers}
|
|
326
|
+
enableUsernamePassword={this.enableUsernamePassword}
|
|
327
|
+
onOAuthSignUp={this.onOAuthSignIn}
|
|
328
|
+
onUsernamePasswordSignUp={this.onUsernamePasswordSignUp}
|
|
329
|
+
/>
|
|
330
|
+
),
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
path: "/signout",
|
|
334
|
+
element: <SignOut />,
|
|
335
|
+
},
|
|
336
|
+
];
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
signOut = async () => {
|
|
340
|
+
const { error } = await this.client.auth.signOut({ scope: "local" });
|
|
341
|
+
if (error) {
|
|
342
|
+
// biome-ignore lint/suspicious: Logging is better than not doing anything
|
|
343
|
+
console.error("Error signing out", error);
|
|
344
|
+
}
|
|
345
|
+
useAuthState.getState().setLoggedOut();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
onPageLoad = async () => {
|
|
349
|
+
const { data, error } = await this.client.auth.getSession();
|
|
350
|
+
|
|
351
|
+
if (!error && data.session) {
|
|
352
|
+
await this.updateUserState(data.session);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const supabaseAuth: AuthenticationProviderInitializer<
|
|
358
|
+
SupabaseAuthenticationConfig
|
|
359
|
+
> = (options) => new SupabaseAuthenticationProvider(options);
|
|
360
|
+
|
|
361
|
+
export default supabaseAuth;
|
|
362
|
+
|
|
363
|
+
const getSupabaseErrorMessage = (error: unknown): string => {
|
|
364
|
+
if (!(error instanceof Error)) {
|
|
365
|
+
return "An unexpected error occurred. Please try again.";
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const errorMessage = error.message;
|
|
369
|
+
|
|
370
|
+
// Map common Supabase error messages to user-friendly messages
|
|
371
|
+
if (errorMessage.includes("Invalid login credentials")) {
|
|
372
|
+
return "The email and password you entered don't match.";
|
|
373
|
+
}
|
|
374
|
+
if (errorMessage.includes("Email not confirmed")) {
|
|
375
|
+
return "Please verify your email address before signing in.";
|
|
376
|
+
}
|
|
377
|
+
if (errorMessage.includes("User already registered")) {
|
|
378
|
+
return "The email address is already used by another account.";
|
|
379
|
+
}
|
|
380
|
+
if (
|
|
381
|
+
errorMessage.includes("Password should be at least") ||
|
|
382
|
+
errorMessage.includes("Password must be at least")
|
|
383
|
+
) {
|
|
384
|
+
return "The password must be at least 6 characters long.";
|
|
385
|
+
}
|
|
386
|
+
if (errorMessage.includes("Invalid email")) {
|
|
387
|
+
return "That email address isn't correct.";
|
|
388
|
+
}
|
|
389
|
+
if (errorMessage.includes("Email rate limit exceeded")) {
|
|
390
|
+
return "Too many requests. Please wait a moment and try again.";
|
|
391
|
+
}
|
|
392
|
+
if (errorMessage.includes("For security purposes")) {
|
|
393
|
+
return "For security purposes, please wait a moment before trying again.";
|
|
394
|
+
}
|
|
395
|
+
if (errorMessage.includes("Unable to validate email address")) {
|
|
396
|
+
return "Unable to validate email address. Please check and try again.";
|
|
397
|
+
}
|
|
398
|
+
if (errorMessage.includes("Signups not allowed")) {
|
|
399
|
+
return "Sign ups are not allowed at this time.";
|
|
400
|
+
}
|
|
401
|
+
if (errorMessage.includes("User not found")) {
|
|
402
|
+
return "That email address doesn't match an existing account.";
|
|
403
|
+
}
|
|
404
|
+
if (errorMessage.includes("New password should be different")) {
|
|
405
|
+
return "Your new password must be different from your current password.";
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Return the original message if no mapping found
|
|
409
|
+
return (
|
|
410
|
+
errorMessage || "An error occurred during authentication. Please try again."
|
|
411
|
+
);
|
|
412
|
+
};
|