@lukoweb/apitogo 0.2.22 → 0.2.24
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 +94 -94
- package/cli.js +13 -13
- package/client.d.ts +8 -8
- package/dist/cli/cli.js +561 -232
- package/dist/declarations/cli/platform/client/platform-api.d.ts +18 -0
- package/dist/declarations/config/validators/InputNavigationSchema.d.ts +7 -7
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +1 -1
- package/docs/cli/commands.md +75 -75
- package/docs/cli/go-live.md +123 -123
- package/docs/cli/overview.md +91 -91
- package/docs/cli/setup.md +141 -141
- package/docs/cli/shape.md +127 -127
- package/docs/components/alert.mdx +130 -130
- package/docs/components/badge.mdx +70 -70
- package/docs/components/button.mdx +132 -132
- package/docs/components/callout.mdx +112 -112
- package/docs/components/card.mdx +104 -104
- package/docs/components/checkbox.mdx +72 -72
- package/docs/components/client-only.mdx +79 -79
- package/docs/components/code-tabs.mdx +179 -179
- package/docs/components/dialog.mdx +167 -167
- package/docs/components/head.mdx +200 -200
- package/docs/components/icons.mdx +27 -27
- package/docs/components/input.mdx +96 -96
- package/docs/components/label.mdx +86 -86
- package/docs/components/link.mdx +242 -242
- package/docs/components/markdown.mdx +151 -151
- package/docs/components/mermaid.mdx +81 -81
- package/docs/components/playground.mdx +85 -85
- package/docs/components/secret.mdx +79 -79
- package/docs/components/select.mdx +176 -176
- package/docs/components/shadcn.mdx +73 -73
- package/docs/components/slider.mdx +108 -108
- package/docs/components/slot.mdx +119 -119
- package/docs/components/stepper.mdx +138 -138
- package/docs/components/switch.mdx +96 -96
- package/docs/components/syntax-highlight.mdx +602 -602
- package/docs/components/textarea.mdx +78 -78
- package/docs/components/tooltip.mdx +195 -195
- package/docs/components/typography.mdx +61 -61
- package/docs/components.mdx +60 -60
- package/docs/concepts/auth-provider-api-identities.md +105 -105
- package/docs/configuration/ai-assistants.md +65 -65
- package/docs/configuration/api-catalog.md +109 -109
- package/docs/configuration/api-reference.md +397 -397
- package/docs/configuration/authentication-auth0.md +181 -181
- package/docs/configuration/authentication-azure-ad.md +245 -245
- package/docs/configuration/authentication-clerk.md +117 -117
- package/docs/configuration/authentication-firebase.md +69 -69
- package/docs/configuration/authentication-pingfederate.md +143 -143
- package/docs/configuration/authentication-supabase.md +233 -233
- package/docs/configuration/authentication.md +159 -159
- package/docs/configuration/build-configuration.mdx +147 -147
- package/docs/configuration/docs.md +282 -282
- package/docs/configuration/footer.mdx +118 -118
- package/docs/configuration/gateway.md +221 -221
- package/docs/configuration/llms.md +90 -90
- package/docs/configuration/make-config.mdx +124 -124
- package/docs/configuration/manifest.md +128 -128
- package/docs/configuration/navigation.mdx +421 -421
- package/docs/configuration/overview.md +166 -166
- package/docs/configuration/plans.md +153 -153
- package/docs/configuration/protected-routes.md +150 -150
- package/docs/configuration/search.md +164 -164
- package/docs/configuration/sentry.mdx +44 -44
- package/docs/configuration/site.md +124 -124
- package/docs/configuration/slots.mdx +125 -125
- package/docs/configuration/vite-config.md +18 -18
- package/docs/custom-plugins.md +285 -285
- package/docs/customization/colors-theme.mdx +278 -278
- package/docs/customization/fonts.md +110 -110
- package/docs/customization/landing-page.md +188 -188
- package/docs/deploy/apache-nginx.md +41 -41
- package/docs/deploy/apitogo.md +17 -17
- package/docs/deploy/cloudflare-pages.md +74 -74
- package/docs/deploy/direct-upload.md +17 -17
- package/docs/deploy/github-pages.md +49 -49
- package/docs/deploy/vercel.md +101 -101
- package/docs/deployment.md +9 -9
- package/docs/extending/events.md +124 -124
- package/docs/guides/custom-pages.md +110 -110
- package/docs/guides/environment-variables.md +94 -94
- package/docs/guides/managing-api-keys-and-identities.md +89 -89
- package/docs/guides/mermaid.mdx +70 -70
- package/docs/guides/navigation-rules.mdx +203 -203
- package/docs/guides/processors.mdx +234 -234
- package/docs/guides/static-files.md +55 -55
- package/docs/guides/transforming-examples.md +156 -156
- package/docs/guides/using-multiple-apis.md +87 -87
- package/docs/markdown/admonitions.md +128 -128
- package/docs/markdown/code-blocks.md +196 -196
- package/docs/markdown/frontmatter.md +173 -173
- package/docs/markdown/mdx.md +68 -68
- package/docs/markdown/overview.md +275 -275
- package/docs/mcp/go-live.md +100 -100
- package/docs/mcp/overview.md +76 -76
- package/docs/mcp/setup.md +173 -173
- package/docs/mcp/shape.md +77 -77
- package/docs/mcp/tools.md +102 -102
- package/docs/plugins.md +24 -24
- package/docs/quickstart.md +72 -72
- package/docs/writing.mdx +72 -72
- package/package.json +1 -1
- package/src/app/ZuploBuildConfig.ts +36 -36
- package/src/app/apitogoBrandTheme.ts +47 -47
- 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 +107 -107
- package/src/app/entry.server.tsx +160 -160
- package/src/app/env.ts +40 -40
- package/src/app/main.css +400 -400
- package/src/app/main.tsx +167 -167
- package/src/app/polyfills.ts +47 -47
- package/src/app/processRoutes.tsx +51 -51
- 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/apitogo-config-core.ts +7 -3
- package/src/config/apitogo-config-loader.browser.ts +17 -17
- package/src/config/apitogo-config-loader.node.ts +88 -88
- package/src/config/apitogo-config-loader.ts +6 -6
- package/src/config/build-apitogo-config.ts +61 -61
- package/src/config/config.ts +74 -74
- package/src/config/create-plugin.ts +71 -71
- package/src/config/default-landing-content.ts +149 -149
- package/src/config/file-exists.ts +6 -6
- package/src/config/gateway-limits.ts +128 -128
- package/src/config/landing-manifest.ts +11 -11
- package/src/config/landing-openapi-showcase.ts +124 -124
- package/src/config/loader.ts +375 -375
- package/src/config/local-manifest.ts +545 -545
- package/src/config/resolve-modules.ts +354 -354
- package/src/config/v2/__fixtures__/scenario-a-docs-only.json +6 -6
- package/src/config/v2/__fixtures__/scenario-b-managed-origin.json +14 -14
- package/src/config/v2/__fixtures__/scenario-c-proxy-oidc.json +15 -15
- package/src/config/v2/__fixtures__/scenario-h-full-stack.json +28 -28
- package/src/config/v2/docs-navigation.ts +72 -72
- package/src/config/v2/errors.ts +42 -42
- package/src/config/v2/gateway-reference.ts +220 -220
- package/src/config/v2/gateway-request.ts +123 -123
- package/src/config/v2/home-redirect.ts +98 -98
- package/src/config/v2/index.ts +126 -126
- package/src/config/v2/init.ts +273 -273
- package/src/config/v2/layout.ts +237 -237
- package/src/config/v2/ledger.ts +235 -235
- package/src/config/v2/link-directory.ts +34 -0
- package/src/config/v2/load.ts +70 -70
- package/src/config/v2/manifest.ts +452 -452
- package/src/config/v2/parse.ts +214 -214
- package/src/config/v2/paths.ts +54 -54
- package/src/config/v2/portal-profile.ts +88 -88
- package/src/config/v2/resolve.ts +188 -188
- package/src/config/v2/rules.ts +714 -714
- package/src/config/v2/schema.ts +245 -245
- package/src/config/v2/services.ts +663 -663
- package/src/config/v2/workspace.ts +3 -2
- package/src/config/v2/wrapper.ts +24 -2
- package/src/config/validators/BuildSchema.ts +95 -95
- package/src/config/validators/HeaderNavigationSchema.ts +44 -44
- package/src/config/validators/InputNavigationSchema.test-d.ts +178 -178
- package/src/config/validators/InputNavigationSchema.ts +206 -206
- package/src/config/validators/ModulesSchema.ts +502 -502
- package/src/config/validators/NavigationSchema.ts +352 -352
- package/src/config/validators/ProtectedRoutesSchema.ts +35 -35
- package/src/config/validators/ZudokuConfig.ts +792 -792
- 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 +83 -83
- 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 -34
- package/src/lib/authentication/AuthenticationPlugin.tsx +33 -33
- package/src/lib/authentication/auth-header-nav.ts +46 -46
- package/src/lib/authentication/authentication.ts +51 -51
- package/src/lib/authentication/components/CallbackHandler.tsx +45 -45
- package/src/lib/authentication/components/OAuthErrorPage.tsx +197 -197
- package/src/lib/authentication/components/ProductionUnlockPage.tsx +26 -26
- package/src/lib/authentication/components/SignIn.tsx +46 -46
- package/src/lib/authentication/components/SignOut.tsx +15 -15
- package/src/lib/authentication/components/SignUp.tsx +48 -48
- package/src/lib/authentication/constants.ts +1 -1
- package/src/lib/authentication/errors.ts +34 -34
- package/src/lib/authentication/header-nav-filter.ts +35 -35
- package/src/lib/authentication/hook.ts +141 -141
- package/src/lib/authentication/providers/auth0.tsx +91 -91
- package/src/lib/authentication/providers/azureb2c.tsx +223 -223
- package/src/lib/authentication/providers/clerk.tsx +224 -224
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +602 -602
- package/src/lib/authentication/providers/dev-portal-constants.ts +38 -38
- package/src/lib/authentication/providers/dev-portal-demo.ts +151 -151
- package/src/lib/authentication/providers/dev-portal-utils.ts +279 -279
- package/src/lib/authentication/providers/dev-portal.tsx +448 -448
- 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 +180 -180
- package/src/lib/authentication/ui/AuthCard.tsx +7 -7
- package/src/lib/authentication/ui/EmailLinkCallbackUi.tsx +129 -129
- package/src/lib/authentication/ui/EmailLinkSentUi.tsx +105 -105
- package/src/lib/authentication/ui/EmailLinkSignInUi.tsx +100 -100
- package/src/lib/authentication/ui/EmailVerificationUi.tsx +128 -128
- package/src/lib/authentication/ui/ZudokuAuthUi.tsx +620 -620
- 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/AiAssistantMenuItems.tsx +102 -102
- package/src/lib/components/AnchorLink.tsx +24 -24
- package/src/lib/components/Autocomplete.tsx +123 -123
- 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/DeveloperHint.tsx +39 -39
- package/src/lib/components/ErrorPage.tsx +26 -26
- package/src/lib/components/Footer.tsx +146 -146
- package/src/lib/components/Framed.tsx +51 -51
- package/src/lib/components/Header.tsx +261 -261
- package/src/lib/components/HeaderAuthActions.tsx +32 -32
- package/src/lib/components/HeaderNavigation.tsx +176 -176
- package/src/lib/components/Heading.tsx +84 -84
- package/src/lib/components/InlineCode.tsx +15 -15
- package/src/lib/components/LandingLayout.tsx +27 -27
- package/src/lib/components/LanguageIcon.tsx +199 -199
- package/src/lib/components/Layout.tsx +59 -59
- package/src/lib/components/Main.tsx +52 -52
- package/src/lib/components/Markdown.tsx +55 -55
- package/src/lib/components/Mermaid.tsx +74 -74
- package/src/lib/components/Meta.tsx +46 -46
- package/src/lib/components/MobileTopNavigation.tsx +283 -283
- package/src/lib/components/MultiSelect.tsx +86 -86
- 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/SiteTitle.tsx +58 -58
- package/src/lib/components/Slot.tsx +65 -65
- package/src/lib/components/Spinner.tsx +5 -5
- package/src/lib/components/StatusPage.tsx +96 -96
- package/src/lib/components/ThemeSwitch.tsx +99 -99
- package/src/lib/components/TopNavigation.tsx +90 -90
- package/src/lib/components/Typography.tsx +12 -12
- package/src/lib/components/Zudoku.tsx +105 -105
- 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 +20 -20
- package/src/lib/components/context/ZudokuReactContext.tsx +17 -17
- package/src/lib/components/index.ts +36 -36
- package/src/lib/components/navigation/Navigation.tsx +51 -51
- package/src/lib/components/navigation/NavigationBadge.tsx +48 -48
- package/src/lib/components/navigation/NavigationCategory.tsx +157 -157
- package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -28
- package/src/lib/components/navigation/NavigationFilterInput.tsx +35 -35
- package/src/lib/components/navigation/NavigationItem.tsx +181 -181
- package/src/lib/components/navigation/NavigationWrapper.tsx +49 -49
- package/src/lib/components/navigation/PoweredByZudoku.tsx +39 -39
- 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 +260 -260
- package/src/lib/core/ZudokuContext.ts +272 -272
- package/src/lib/core/plugins.ts +139 -139
- 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/ErrorMessage.tsx +50 -50
- 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/Catalog.tsx +80 -80
- package/src/lib/plugins/api-catalog/index.tsx +119 -119
- package/src/lib/plugins/api-keys/CreateApiKey.tsx +128 -128
- package/src/lib/plugins/api-keys/CreateApiKeyDialog.tsx +49 -49
- package/src/lib/plugins/api-keys/ProtectedRoute.tsx +33 -33
- package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +72 -72
- package/src/lib/plugins/api-keys/index.tsx +274 -274
- package/src/lib/plugins/api-keys/settings/ApiKeyItem.tsx +342 -342
- package/src/lib/plugins/api-keys/settings/ApiKeyList.tsx +67 -67
- package/src/lib/plugins/api-keys/settings/RevealApiKey.tsx +127 -127
- package/src/lib/plugins/custom-pages/index.tsx +32 -32
- package/src/lib/plugins/markdown/MdxPage.tsx +260 -260
- 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 +82 -82
- package/src/lib/plugins/openapi/ApiHeader.tsx +96 -96
- package/src/lib/plugins/openapi/CollapsibleCode.tsx +88 -88
- package/src/lib/plugins/openapi/ColorizedParam.tsx +115 -115
- package/src/lib/plugins/openapi/DownloadSchemaButton.tsx +94 -94
- package/src/lib/plugins/openapi/Endpoint.tsx +85 -85
- package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +52 -52
- 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/OperationListItem.tsx +183 -183
- 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/RequestBodySidecarBox.tsx +63 -63
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +120 -120
- package/src/lib/plugins/openapi/SchemaInfo.tsx +344 -344
- package/src/lib/plugins/openapi/SchemaList.tsx +113 -113
- package/src/lib/plugins/openapi/Sidecar.tsx +314 -314
- package/src/lib/plugins/openapi/SidecarBox.tsx +57 -57
- package/src/lib/plugins/openapi/SidecarExamples.tsx +177 -177
- 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/EnumValues.tsx +58 -58
- package/src/lib/plugins/openapi/components/NonHighlightedCode.tsx +22 -22
- package/src/lib/plugins/openapi/components/ResponseContent.tsx +117 -117
- 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/BodyPanel.tsx +274 -274
- package/src/lib/plugins/openapi/playground/CollapsibleHeader.tsx +51 -51
- package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +60 -60
- package/src/lib/plugins/openapi/playground/Headers.tsx +194 -194
- package/src/lib/plugins/openapi/playground/IdentityDialog.tsx +75 -75
- package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +38 -38
- package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +45 -45
- package/src/lib/plugins/openapi/playground/PathParams.tsx +53 -53
- package/src/lib/plugins/openapi/playground/Playground.tsx +602 -602
- package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +62 -62
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +151 -151
- package/src/lib/plugins/openapi/playground/RequestLoginDialog.tsx +70 -70
- 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/MultipartField.tsx +91 -91
- 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/AudioPlayer.tsx +50 -50
- package/src/lib/plugins/openapi/playground/result-panel/ResponseStatusBar.tsx +117 -117
- package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +355 -355
- package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +106 -106
- 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/injectApitogoGatewayServer.ts +36 -36
- 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/SchemaPropertyItem.tsx +189 -189
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +299 -299
- 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/IndexingDialog.tsx +162 -162
- package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +202 -202
- package/src/lib/plugins/search-pagefind/ResultList.tsx +122 -122
- 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/shiki.ts +179 -179
- package/src/lib/testing/index.tsx +147 -147
- package/src/lib/ui/Accordion.tsx +55 -55
- package/src/lib/ui/ActionButton.tsx +21 -21
- 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/Command.tsx +191 -191
- 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/MdxComponents.tsx +150 -150
- 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 +75 -75
- package/src/lib/util/useScrollToTop.ts +18 -18
- package/src/types.d.ts +66 -66
- package/src/vite/api/SchemaManager.ts +371 -371
- package/src/vite/api/schema-codegen.ts +163 -163
- package/src/vite/build.ts +237 -237
- package/src/vite/config.ts +273 -273
- 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 +29 -29
- package/src/vite/dev-server.ts +294 -294
- package/src/vite/html.ts +52 -52
- package/src/vite/index.ts +2 -2
- package/src/vite/llms.ts +99 -99
- 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/pagefind-dev-stub.ts +17 -17
- package/src/vite/plugin-api-keys.ts +55 -55
- package/src/vite/plugin-api.ts +328 -328
- package/src/vite/plugin-auth.ts +41 -41
- package/src/vite/plugin-component.ts +45 -45
- package/src/vite/plugin-config-reload.ts +47 -47
- package/src/vite/plugin-config.ts +131 -131
- package/src/vite/plugin-custom-pages.ts +36 -36
- package/src/vite/plugin-doc-metadata.ts +45 -45
- package/src/vite/plugin-docs.ts +205 -205
- package/src/vite/plugin-local-manifest.ts +100 -100
- package/src/vite/plugin-markdown-export.ts +214 -214
- package/src/vite/plugin-mdx.ts +149 -149
- package/src/vite/plugin-metadata.ts +30 -30
- package/src/vite/plugin-modules.ts +188 -188
- package/src/vite/plugin-navigation.ts +106 -106
- package/src/vite/plugin-offroot-resolve.ts +67 -67
- package/src/vite/plugin-search.ts +59 -59
- package/src/vite/plugin-shiki-register.ts +97 -97
- package/src/vite/plugin-theme.ts +458 -458
- package/src/vite/plugin.ts +48 -48
- package/src/vite/prerender/prerender.ts +242 -242
- 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/watch-ignore.ts +70 -70
- 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/dist/declarations/cli/platform/client/deploy-server.d.ts +0 -16
package/src/config/v2/ledger.ts
CHANGED
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { ApitogoError } from "../../lib/util/invariant.js";
|
|
4
|
-
import type { StatePaths } from "./paths.js";
|
|
5
|
-
|
|
6
|
-
/** Version of the `state.json` format this build reads and writes. A newer file is refused, never overwritten. */
|
|
7
|
-
export const LEDGER_VERSION = 1;
|
|
8
|
-
|
|
9
|
-
export type StepStatus = "running" | "succeeded" | "failed";
|
|
10
|
-
|
|
11
|
-
/** One step of one service, for example `portal` / `build`. */
|
|
12
|
-
export type StepRecord = {
|
|
13
|
-
status: StepStatus;
|
|
14
|
-
/** Hash of the inputs the step ran with. An equal hash means the result is still valid. */
|
|
15
|
-
hash?: string;
|
|
16
|
-
startedAt: string;
|
|
17
|
-
finishedAt?: string;
|
|
18
|
-
/** Why the step failed. */
|
|
19
|
-
error?: string;
|
|
20
|
-
/** Public facts the step produced, such as a deployed URL. */
|
|
21
|
-
output?: Record<string, string>;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type Ledger = {
|
|
25
|
-
version: typeof LEDGER_VERSION;
|
|
26
|
-
services: Record<string, { steps: Record<string, StepRecord> }>;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export class LedgerError extends ApitogoError {
|
|
30
|
-
constructor(
|
|
31
|
-
public readonly code: "LEDGER_CORRUPT" | "LEDGER_VERSION_UNSUPPORTED",
|
|
32
|
-
message: string,
|
|
33
|
-
developerHint?: string,
|
|
34
|
-
) {
|
|
35
|
-
super(message, { developerHint, title: "Cannot read the project state" });
|
|
36
|
-
this.name = "LedgerError";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const emptyLedger = (): Ledger => ({
|
|
41
|
-
version: LEDGER_VERSION,
|
|
42
|
-
services: {},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
/** Reads `state.json`. A missing file is an empty ledger. A corrupt or newer file raises a {@link LedgerError}. */
|
|
46
|
-
export async function readLedger(
|
|
47
|
-
state: Pick<StatePaths, "file">,
|
|
48
|
-
): Promise<Ledger> {
|
|
49
|
-
let text: string;
|
|
50
|
-
try {
|
|
51
|
-
text = await readFile(state.file, "utf8");
|
|
52
|
-
} catch (error) {
|
|
53
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
|
54
|
-
return emptyLedger();
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let raw: unknown;
|
|
59
|
-
try {
|
|
60
|
-
raw = JSON.parse(text);
|
|
61
|
-
} catch {
|
|
62
|
-
throw corrupt(state.file, "is not valid JSON");
|
|
63
|
-
}
|
|
64
|
-
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
65
|
-
throw corrupt(state.file, "is not an object");
|
|
66
|
-
}
|
|
67
|
-
const { version, services } = raw as Record<string, unknown>;
|
|
68
|
-
if (typeof version !== "number") throw corrupt(state.file, "has no version");
|
|
69
|
-
if (version > LEDGER_VERSION) {
|
|
70
|
-
throw new LedgerError(
|
|
71
|
-
"LEDGER_VERSION_UNSUPPORTED",
|
|
72
|
-
`${state.file} has version ${version} but this CLI supports ${LEDGER_VERSION}.`,
|
|
73
|
-
"Upgrade @lukoweb/apitogo.",
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (version !== LEDGER_VERSION)
|
|
77
|
-
throw corrupt(state.file, `has unknown version ${version}`);
|
|
78
|
-
if (
|
|
79
|
-
typeof services !== "object" ||
|
|
80
|
-
services === null ||
|
|
81
|
-
Array.isArray(services)
|
|
82
|
-
) {
|
|
83
|
-
throw corrupt(state.file, "has no services object");
|
|
84
|
-
}
|
|
85
|
-
return { version: LEDGER_VERSION, services: services as Ledger["services"] };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function corrupt(file: string, why: string): LedgerError {
|
|
89
|
-
return new LedgerError(
|
|
90
|
-
"LEDGER_CORRUPT",
|
|
91
|
-
`${file} ${why}.`,
|
|
92
|
-
"It only holds regenerable state: delete it and run the command again.",
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Replaces `state.json` atomically, so a crash never leaves a half-written file. */
|
|
97
|
-
export async function writeLedger(
|
|
98
|
-
state: Pick<StatePaths, "file">,
|
|
99
|
-
ledger: Ledger,
|
|
100
|
-
): Promise<void> {
|
|
101
|
-
await mkdir(path.dirname(state.file), { recursive: true });
|
|
102
|
-
const temporary = `${state.file}.${process.pid}.tmp`;
|
|
103
|
-
try {
|
|
104
|
-
await writeFile(temporary, `${JSON.stringify(ledger, null, 2)}\n`, "utf8");
|
|
105
|
-
await rename(temporary, state.file);
|
|
106
|
-
} catch (error) {
|
|
107
|
-
await rm(temporary, { force: true });
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function stepOf(
|
|
113
|
-
ledger: Ledger,
|
|
114
|
-
service: string,
|
|
115
|
-
step: string,
|
|
116
|
-
): StepRecord | undefined {
|
|
117
|
-
return ledger.services[service]?.steps[step];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function withStep(
|
|
121
|
-
ledger: Ledger,
|
|
122
|
-
service: string,
|
|
123
|
-
step: string,
|
|
124
|
-
record: StepRecord,
|
|
125
|
-
): Ledger {
|
|
126
|
-
return {
|
|
127
|
-
...ledger,
|
|
128
|
-
services: {
|
|
129
|
-
...ledger.services,
|
|
130
|
-
[service]: {
|
|
131
|
-
steps: { ...ledger.services[service]?.steps, [step]: record },
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export type PipelineStep = {
|
|
138
|
-
name: string;
|
|
139
|
-
/** Hash of this step's inputs. */
|
|
140
|
-
hash: () => string | Promise<string>;
|
|
141
|
-
run: () => Promise<Record<string, string> | undefined | void>;
|
|
142
|
-
/** Extra proof the previous result still exists (for example the build folder). Defaults to true. */
|
|
143
|
-
stillValid?: () => Promise<boolean>;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
export type StepOutcome = {
|
|
147
|
-
step: string;
|
|
148
|
-
skipped: boolean;
|
|
149
|
-
output?: Record<string, string>;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Runs a service's steps in order against the ledger. A step is skipped when it already succeeded with the same
|
|
154
|
-
* hash and nothing before it ran again. A failure is recorded with its message and rethrown; the next run starts
|
|
155
|
-
* at that step, because the steps before it succeeded and are skipped.
|
|
156
|
-
*/
|
|
157
|
-
export async function runPipeline(
|
|
158
|
-
state: Pick<StatePaths, "file">,
|
|
159
|
-
service: string,
|
|
160
|
-
steps: readonly PipelineStep[],
|
|
161
|
-
options: { force?: boolean; now?: () => Date } = {},
|
|
162
|
-
): Promise<StepOutcome[]> {
|
|
163
|
-
const now = options.now ?? (() => new Date());
|
|
164
|
-
let ledger = await readLedger(state);
|
|
165
|
-
const outcomes: StepOutcome[] = [];
|
|
166
|
-
let upstreamRan = false;
|
|
167
|
-
|
|
168
|
-
for (const step of steps) {
|
|
169
|
-
const hash = await step.hash();
|
|
170
|
-
const previous = stepOf(ledger, service, step.name);
|
|
171
|
-
const reusable =
|
|
172
|
-
!options.force &&
|
|
173
|
-
!upstreamRan &&
|
|
174
|
-
previous?.status === "succeeded" &&
|
|
175
|
-
previous.hash === hash &&
|
|
176
|
-
(step.stillValid ? await step.stillValid() : true);
|
|
177
|
-
|
|
178
|
-
if (reusable) {
|
|
179
|
-
outcomes.push({
|
|
180
|
-
step: step.name,
|
|
181
|
-
skipped: true,
|
|
182
|
-
output: previous?.output,
|
|
183
|
-
});
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
upstreamRan = true;
|
|
188
|
-
const startedAt = now().toISOString();
|
|
189
|
-
ledger = withStep(ledger, service, step.name, {
|
|
190
|
-
status: "running",
|
|
191
|
-
hash,
|
|
192
|
-
startedAt,
|
|
193
|
-
});
|
|
194
|
-
await writeLedger(state, ledger);
|
|
195
|
-
try {
|
|
196
|
-
const output = (await step.run()) || undefined;
|
|
197
|
-
ledger = withStep(ledger, service, step.name, {
|
|
198
|
-
status: "succeeded",
|
|
199
|
-
hash,
|
|
200
|
-
startedAt,
|
|
201
|
-
finishedAt: now().toISOString(),
|
|
202
|
-
...(output ? { output } : {}),
|
|
203
|
-
});
|
|
204
|
-
await writeLedger(state, ledger);
|
|
205
|
-
outcomes.push({ step: step.name, skipped: false, output });
|
|
206
|
-
} catch (error) {
|
|
207
|
-
ledger = withStep(ledger, service, step.name, {
|
|
208
|
-
status: "failed",
|
|
209
|
-
hash,
|
|
210
|
-
startedAt,
|
|
211
|
-
finishedAt: now().toISOString(),
|
|
212
|
-
error: error instanceof Error ? error.message : String(error),
|
|
213
|
-
});
|
|
214
|
-
await writeLedger(state, ledger);
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return outcomes;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/** Forgets the recorded steps of services that no longer exist on the platform. Other services keep their entries. */
|
|
222
|
-
export async function clearServices(
|
|
223
|
-
state: Pick<StatePaths, "file">,
|
|
224
|
-
services: readonly string[],
|
|
225
|
-
): Promise<void> {
|
|
226
|
-
const ledger = await readLedger(state);
|
|
227
|
-
const remaining = Object.fromEntries(
|
|
228
|
-
Object.entries(ledger.services).filter(
|
|
229
|
-
([name]) => !services.includes(name),
|
|
230
|
-
),
|
|
231
|
-
);
|
|
232
|
-
if (Object.keys(remaining).length === Object.keys(ledger.services).length)
|
|
233
|
-
return;
|
|
234
|
-
await writeLedger(state, { ...ledger, services: remaining });
|
|
235
|
-
}
|
|
1
|
+
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ApitogoError } from "../../lib/util/invariant.js";
|
|
4
|
+
import type { StatePaths } from "./paths.js";
|
|
5
|
+
|
|
6
|
+
/** Version of the `state.json` format this build reads and writes. A newer file is refused, never overwritten. */
|
|
7
|
+
export const LEDGER_VERSION = 1;
|
|
8
|
+
|
|
9
|
+
export type StepStatus = "running" | "succeeded" | "failed";
|
|
10
|
+
|
|
11
|
+
/** One step of one service, for example `portal` / `build`. */
|
|
12
|
+
export type StepRecord = {
|
|
13
|
+
status: StepStatus;
|
|
14
|
+
/** Hash of the inputs the step ran with. An equal hash means the result is still valid. */
|
|
15
|
+
hash?: string;
|
|
16
|
+
startedAt: string;
|
|
17
|
+
finishedAt?: string;
|
|
18
|
+
/** Why the step failed. */
|
|
19
|
+
error?: string;
|
|
20
|
+
/** Public facts the step produced, such as a deployed URL. */
|
|
21
|
+
output?: Record<string, string>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type Ledger = {
|
|
25
|
+
version: typeof LEDGER_VERSION;
|
|
26
|
+
services: Record<string, { steps: Record<string, StepRecord> }>;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export class LedgerError extends ApitogoError {
|
|
30
|
+
constructor(
|
|
31
|
+
public readonly code: "LEDGER_CORRUPT" | "LEDGER_VERSION_UNSUPPORTED",
|
|
32
|
+
message: string,
|
|
33
|
+
developerHint?: string,
|
|
34
|
+
) {
|
|
35
|
+
super(message, { developerHint, title: "Cannot read the project state" });
|
|
36
|
+
this.name = "LedgerError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const emptyLedger = (): Ledger => ({
|
|
41
|
+
version: LEDGER_VERSION,
|
|
42
|
+
services: {},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** Reads `state.json`. A missing file is an empty ledger. A corrupt or newer file raises a {@link LedgerError}. */
|
|
46
|
+
export async function readLedger(
|
|
47
|
+
state: Pick<StatePaths, "file">,
|
|
48
|
+
): Promise<Ledger> {
|
|
49
|
+
let text: string;
|
|
50
|
+
try {
|
|
51
|
+
text = await readFile(state.file, "utf8");
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
|
54
|
+
return emptyLedger();
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let raw: unknown;
|
|
59
|
+
try {
|
|
60
|
+
raw = JSON.parse(text);
|
|
61
|
+
} catch {
|
|
62
|
+
throw corrupt(state.file, "is not valid JSON");
|
|
63
|
+
}
|
|
64
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
65
|
+
throw corrupt(state.file, "is not an object");
|
|
66
|
+
}
|
|
67
|
+
const { version, services } = raw as Record<string, unknown>;
|
|
68
|
+
if (typeof version !== "number") throw corrupt(state.file, "has no version");
|
|
69
|
+
if (version > LEDGER_VERSION) {
|
|
70
|
+
throw new LedgerError(
|
|
71
|
+
"LEDGER_VERSION_UNSUPPORTED",
|
|
72
|
+
`${state.file} has version ${version} but this CLI supports ${LEDGER_VERSION}.`,
|
|
73
|
+
"Upgrade @lukoweb/apitogo.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (version !== LEDGER_VERSION)
|
|
77
|
+
throw corrupt(state.file, `has unknown version ${version}`);
|
|
78
|
+
if (
|
|
79
|
+
typeof services !== "object" ||
|
|
80
|
+
services === null ||
|
|
81
|
+
Array.isArray(services)
|
|
82
|
+
) {
|
|
83
|
+
throw corrupt(state.file, "has no services object");
|
|
84
|
+
}
|
|
85
|
+
return { version: LEDGER_VERSION, services: services as Ledger["services"] };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function corrupt(file: string, why: string): LedgerError {
|
|
89
|
+
return new LedgerError(
|
|
90
|
+
"LEDGER_CORRUPT",
|
|
91
|
+
`${file} ${why}.`,
|
|
92
|
+
"It only holds regenerable state: delete it and run the command again.",
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Replaces `state.json` atomically, so a crash never leaves a half-written file. */
|
|
97
|
+
export async function writeLedger(
|
|
98
|
+
state: Pick<StatePaths, "file">,
|
|
99
|
+
ledger: Ledger,
|
|
100
|
+
): Promise<void> {
|
|
101
|
+
await mkdir(path.dirname(state.file), { recursive: true });
|
|
102
|
+
const temporary = `${state.file}.${process.pid}.tmp`;
|
|
103
|
+
try {
|
|
104
|
+
await writeFile(temporary, `${JSON.stringify(ledger, null, 2)}\n`, "utf8");
|
|
105
|
+
await rename(temporary, state.file);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
await rm(temporary, { force: true });
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function stepOf(
|
|
113
|
+
ledger: Ledger,
|
|
114
|
+
service: string,
|
|
115
|
+
step: string,
|
|
116
|
+
): StepRecord | undefined {
|
|
117
|
+
return ledger.services[service]?.steps[step];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function withStep(
|
|
121
|
+
ledger: Ledger,
|
|
122
|
+
service: string,
|
|
123
|
+
step: string,
|
|
124
|
+
record: StepRecord,
|
|
125
|
+
): Ledger {
|
|
126
|
+
return {
|
|
127
|
+
...ledger,
|
|
128
|
+
services: {
|
|
129
|
+
...ledger.services,
|
|
130
|
+
[service]: {
|
|
131
|
+
steps: { ...ledger.services[service]?.steps, [step]: record },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type PipelineStep = {
|
|
138
|
+
name: string;
|
|
139
|
+
/** Hash of this step's inputs. */
|
|
140
|
+
hash: () => string | Promise<string>;
|
|
141
|
+
run: () => Promise<Record<string, string> | undefined | void>;
|
|
142
|
+
/** Extra proof the previous result still exists (for example the build folder). Defaults to true. */
|
|
143
|
+
stillValid?: () => Promise<boolean>;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type StepOutcome = {
|
|
147
|
+
step: string;
|
|
148
|
+
skipped: boolean;
|
|
149
|
+
output?: Record<string, string>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Runs a service's steps in order against the ledger. A step is skipped when it already succeeded with the same
|
|
154
|
+
* hash and nothing before it ran again. A failure is recorded with its message and rethrown; the next run starts
|
|
155
|
+
* at that step, because the steps before it succeeded and are skipped.
|
|
156
|
+
*/
|
|
157
|
+
export async function runPipeline(
|
|
158
|
+
state: Pick<StatePaths, "file">,
|
|
159
|
+
service: string,
|
|
160
|
+
steps: readonly PipelineStep[],
|
|
161
|
+
options: { force?: boolean; now?: () => Date } = {},
|
|
162
|
+
): Promise<StepOutcome[]> {
|
|
163
|
+
const now = options.now ?? (() => new Date());
|
|
164
|
+
let ledger = await readLedger(state);
|
|
165
|
+
const outcomes: StepOutcome[] = [];
|
|
166
|
+
let upstreamRan = false;
|
|
167
|
+
|
|
168
|
+
for (const step of steps) {
|
|
169
|
+
const hash = await step.hash();
|
|
170
|
+
const previous = stepOf(ledger, service, step.name);
|
|
171
|
+
const reusable =
|
|
172
|
+
!options.force &&
|
|
173
|
+
!upstreamRan &&
|
|
174
|
+
previous?.status === "succeeded" &&
|
|
175
|
+
previous.hash === hash &&
|
|
176
|
+
(step.stillValid ? await step.stillValid() : true);
|
|
177
|
+
|
|
178
|
+
if (reusable) {
|
|
179
|
+
outcomes.push({
|
|
180
|
+
step: step.name,
|
|
181
|
+
skipped: true,
|
|
182
|
+
output: previous?.output,
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
upstreamRan = true;
|
|
188
|
+
const startedAt = now().toISOString();
|
|
189
|
+
ledger = withStep(ledger, service, step.name, {
|
|
190
|
+
status: "running",
|
|
191
|
+
hash,
|
|
192
|
+
startedAt,
|
|
193
|
+
});
|
|
194
|
+
await writeLedger(state, ledger);
|
|
195
|
+
try {
|
|
196
|
+
const output = (await step.run()) || undefined;
|
|
197
|
+
ledger = withStep(ledger, service, step.name, {
|
|
198
|
+
status: "succeeded",
|
|
199
|
+
hash,
|
|
200
|
+
startedAt,
|
|
201
|
+
finishedAt: now().toISOString(),
|
|
202
|
+
...(output ? { output } : {}),
|
|
203
|
+
});
|
|
204
|
+
await writeLedger(state, ledger);
|
|
205
|
+
outcomes.push({ step: step.name, skipped: false, output });
|
|
206
|
+
} catch (error) {
|
|
207
|
+
ledger = withStep(ledger, service, step.name, {
|
|
208
|
+
status: "failed",
|
|
209
|
+
hash,
|
|
210
|
+
startedAt,
|
|
211
|
+
finishedAt: now().toISOString(),
|
|
212
|
+
error: error instanceof Error ? error.message : String(error),
|
|
213
|
+
});
|
|
214
|
+
await writeLedger(state, ledger);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return outcomes;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Forgets the recorded steps of services that no longer exist on the platform. Other services keep their entries. */
|
|
222
|
+
export async function clearServices(
|
|
223
|
+
state: Pick<StatePaths, "file">,
|
|
224
|
+
services: readonly string[],
|
|
225
|
+
): Promise<void> {
|
|
226
|
+
const ledger = await readLedger(state);
|
|
227
|
+
const remaining = Object.fromEntries(
|
|
228
|
+
Object.entries(ledger.services).filter(
|
|
229
|
+
([name]) => !services.includes(name),
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
if (Object.keys(remaining).length === Object.keys(ledger.services).length)
|
|
233
|
+
return;
|
|
234
|
+
await writeLedger(state, { ...ledger, services: remaining });
|
|
235
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { symlink } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
type SymlinkFn = (
|
|
4
|
+
target: string,
|
|
5
|
+
path: string,
|
|
6
|
+
type?: "dir" | "file" | "junction",
|
|
7
|
+
) => Promise<void>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Links `link` to the directory `target`. On Windows a directory symlink needs a privilege most accounts do not
|
|
11
|
+
* have (Developer Mode or an elevated shell) and fails with EPERM; a junction needs none and behaves the same for
|
|
12
|
+
* reading, so it is used then. Any other failure, and EPERM on another system, is not hidden.
|
|
13
|
+
*/
|
|
14
|
+
export async function linkDirectory(
|
|
15
|
+
target: string,
|
|
16
|
+
link: string,
|
|
17
|
+
options: { symlinkFn?: SymlinkFn; platform?: NodeJS.Platform } = {},
|
|
18
|
+
): Promise<"symlink" | "junction"> {
|
|
19
|
+
const create = options.symlinkFn ?? symlink;
|
|
20
|
+
const platform = options.platform ?? process.platform;
|
|
21
|
+
try {
|
|
22
|
+
await create(target, link, "dir");
|
|
23
|
+
return "symlink";
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (
|
|
26
|
+
platform !== "win32" ||
|
|
27
|
+
(error as NodeJS.ErrnoException).code !== "EPERM"
|
|
28
|
+
)
|
|
29
|
+
throw error;
|
|
30
|
+
// A junction takes an absolute target.
|
|
31
|
+
await create(target, link, "junction");
|
|
32
|
+
return "junction";
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/config/v2/load.ts
CHANGED
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { APITOGO_CONFIG_FILENAME } from "../apitogo-config-core.js";
|
|
4
|
-
import { ConfigError } from "./errors.js";
|
|
5
|
-
import { parseConfigV2 } from "./parse.js";
|
|
6
|
-
import { type ResolvedConfigV2, resolveConfigV2 } from "./resolve.js";
|
|
7
|
-
import type { ConfigV2 } from "./schema.js";
|
|
8
|
-
|
|
9
|
-
export type LoadedConfigV2 = {
|
|
10
|
-
config: ConfigV2;
|
|
11
|
-
resolved: ResolvedConfigV2;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
/** `JSON.parse` failures carry a position in different shapes across Node versions; turn it into line and column. */
|
|
15
|
-
function describeJsonError(error: unknown, text: string): string {
|
|
16
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
-
const lineColumn = /line (\d+) column (\d+)/.exec(message);
|
|
18
|
-
if (lineColumn) return `${message}`;
|
|
19
|
-
|
|
20
|
-
const position = /position (\d+)/.exec(message);
|
|
21
|
-
if (!position) return message;
|
|
22
|
-
|
|
23
|
-
const offset = Number(position[1]);
|
|
24
|
-
const before = text.slice(0, offset);
|
|
25
|
-
const line = before.split("\n").length;
|
|
26
|
-
const column = offset - before.lastIndexOf("\n");
|
|
27
|
-
return `${message} (line ${line} column ${column})`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Reads, validates and resolves a v2 config. `pathOrDir` is either the config file or the directory that
|
|
32
|
-
* contains `apitogo.config.json`. Invalid JSON, unknown keys, inline secrets and every other schema problem
|
|
33
|
-
* raise a {@link ConfigError}; nothing is silently dropped.
|
|
34
|
-
*/
|
|
35
|
-
export function loadConfigV2(pathOrDir: string): LoadedConfigV2 {
|
|
36
|
-
const resolvedPath = path.resolve(pathOrDir);
|
|
37
|
-
const configPath =
|
|
38
|
-
existsSync(resolvedPath) && statSync(resolvedPath).isDirectory()
|
|
39
|
-
? path.join(resolvedPath, APITOGO_CONFIG_FILENAME)
|
|
40
|
-
: resolvedPath;
|
|
41
|
-
|
|
42
|
-
if (!existsSync(configPath)) {
|
|
43
|
-
throw new ConfigError(path.basename(configPath), [
|
|
44
|
-
{
|
|
45
|
-
code: "CONFIG_NOT_FOUND",
|
|
46
|
-
path: "",
|
|
47
|
-
message: `No config file at ${configPath}.`,
|
|
48
|
-
hint: "Run `apitogo init` to create one.",
|
|
49
|
-
},
|
|
50
|
-
]);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const text = readFileSync(configPath, "utf8");
|
|
54
|
-
let raw: unknown;
|
|
55
|
-
try {
|
|
56
|
-
raw = JSON.parse(text);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
throw new ConfigError(path.basename(configPath), [
|
|
59
|
-
{
|
|
60
|
-
code: "CONFIG_INVALID_JSON",
|
|
61
|
-
path: "",
|
|
62
|
-
message: describeJsonError(error, text),
|
|
63
|
-
hint: "Fix the JSON syntax; comments and trailing commas are not allowed.",
|
|
64
|
-
},
|
|
65
|
-
]);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const config = parseConfigV2(raw, path.basename(configPath));
|
|
69
|
-
return { config, resolved: resolveConfigV2(config, configPath) };
|
|
70
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { APITOGO_CONFIG_FILENAME } from "../apitogo-config-core.js";
|
|
4
|
+
import { ConfigError } from "./errors.js";
|
|
5
|
+
import { parseConfigV2 } from "./parse.js";
|
|
6
|
+
import { type ResolvedConfigV2, resolveConfigV2 } from "./resolve.js";
|
|
7
|
+
import type { ConfigV2 } from "./schema.js";
|
|
8
|
+
|
|
9
|
+
export type LoadedConfigV2 = {
|
|
10
|
+
config: ConfigV2;
|
|
11
|
+
resolved: ResolvedConfigV2;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** `JSON.parse` failures carry a position in different shapes across Node versions; turn it into line and column. */
|
|
15
|
+
function describeJsonError(error: unknown, text: string): string {
|
|
16
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
+
const lineColumn = /line (\d+) column (\d+)/.exec(message);
|
|
18
|
+
if (lineColumn) return `${message}`;
|
|
19
|
+
|
|
20
|
+
const position = /position (\d+)/.exec(message);
|
|
21
|
+
if (!position) return message;
|
|
22
|
+
|
|
23
|
+
const offset = Number(position[1]);
|
|
24
|
+
const before = text.slice(0, offset);
|
|
25
|
+
const line = before.split("\n").length;
|
|
26
|
+
const column = offset - before.lastIndexOf("\n");
|
|
27
|
+
return `${message} (line ${line} column ${column})`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reads, validates and resolves a v2 config. `pathOrDir` is either the config file or the directory that
|
|
32
|
+
* contains `apitogo.config.json`. Invalid JSON, unknown keys, inline secrets and every other schema problem
|
|
33
|
+
* raise a {@link ConfigError}; nothing is silently dropped.
|
|
34
|
+
*/
|
|
35
|
+
export function loadConfigV2(pathOrDir: string): LoadedConfigV2 {
|
|
36
|
+
const resolvedPath = path.resolve(pathOrDir);
|
|
37
|
+
const configPath =
|
|
38
|
+
existsSync(resolvedPath) && statSync(resolvedPath).isDirectory()
|
|
39
|
+
? path.join(resolvedPath, APITOGO_CONFIG_FILENAME)
|
|
40
|
+
: resolvedPath;
|
|
41
|
+
|
|
42
|
+
if (!existsSync(configPath)) {
|
|
43
|
+
throw new ConfigError(path.basename(configPath), [
|
|
44
|
+
{
|
|
45
|
+
code: "CONFIG_NOT_FOUND",
|
|
46
|
+
path: "",
|
|
47
|
+
message: `No config file at ${configPath}.`,
|
|
48
|
+
hint: "Run `apitogo init` to create one.",
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const text = readFileSync(configPath, "utf8");
|
|
54
|
+
let raw: unknown;
|
|
55
|
+
try {
|
|
56
|
+
raw = JSON.parse(text);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new ConfigError(path.basename(configPath), [
|
|
59
|
+
{
|
|
60
|
+
code: "CONFIG_INVALID_JSON",
|
|
61
|
+
path: "",
|
|
62
|
+
message: describeJsonError(error, text),
|
|
63
|
+
hint: "Fix the JSON syntax; comments and trailing commas are not allowed.",
|
|
64
|
+
},
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const config = parseConfigV2(raw, path.basename(configPath));
|
|
69
|
+
return { config, resolved: resolveConfigV2(config, configPath) };
|
|
70
|
+
}
|