@lukoweb/apitogo 0.2.54 → 0.2.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/cli/cli.js +2196 -1352
  2. package/dist/declarations/cli/platform/client/platform-api.d.ts +29 -0
  3. package/dist/declarations/cli/platform/helpers/platform-plan-catalog.d.ts +6 -0
  4. package/dist/declarations/config/validators/ZudokuConfig.d.ts +2 -0
  5. package/dist/declarations/lib/authentication/providers/openid.d.ts +2 -1
  6. package/dist/declarations/lib/plugins/markdown/index.d.ts +2 -1
  7. package/dist/flat-config.d.ts +1 -0
  8. package/docs/cli/commands.md +50 -32
  9. package/docs/cli/go-live.md +66 -93
  10. package/docs/cli/overview.md +18 -17
  11. package/docs/cli/setup.md +130 -141
  12. package/docs/cli/shape.md +94 -11
  13. package/docs/cli/walkthrough.md +1 -1
  14. package/docs/components/slot.mdx +116 -119
  15. package/docs/configuration/api-reference.md +98 -397
  16. package/docs/configuration/authentication.md +118 -159
  17. package/docs/configuration/footer.mdx +89 -118
  18. package/docs/configuration/make-config.mdx +2 -2
  19. package/docs/configuration/overview.md +97 -166
  20. package/docs/configuration/plans.md +100 -110
  21. package/docs/configuration/portal.md +116 -0
  22. package/docs/customization/colors-theme.mdx +255 -278
  23. package/docs/customization/fonts.md +105 -110
  24. package/docs/customization/landing-page.md +189 -188
  25. package/docs/deploy/apitogo.md +13 -17
  26. package/docs/deployment.md +15 -9
  27. package/docs/guides/stripe-setup.mdx +123 -0
  28. package/docs/markdown/frontmatter.md +172 -173
  29. package/docs/mcp/go-live.md +100 -100
  30. package/docs/mcp/overview.md +76 -76
  31. package/docs/quickstart.md +88 -72
  32. package/docs/writing.mdx +68 -72
  33. package/package.json +1 -1
  34. package/src/config/v2/docs-navigation.ts +7 -2
  35. package/src/config/v2/index.ts +1 -0
  36. package/src/config/v2/resolve.ts +28 -0
  37. package/src/config/v2/rules.ts +32 -1
  38. package/src/config/v2/schema.ts +9 -0
  39. package/src/config/v2/services.ts +90 -15
  40. package/src/config/v2/starter-landing.ts +41 -0
  41. package/src/config/v2/wrapper.ts +141 -7
  42. package/src/config/validators/ZudokuConfig.ts +794 -792
  43. package/src/lib/authentication/providers/openid.tsx +6 -1
  44. package/src/lib/plugins/markdown/index.tsx +86 -82
  45. package/src/vite/dev-server.ts +9 -1
  46. package/src/vite/dev-ssr-config.ts +32 -0
  47. package/src/vite/plugin-config.ts +4 -2
  48. package/src/vite/plugin-docs.ts +213 -205
  49. package/docs/concepts/auth-provider-api-identities.md +0 -105
  50. package/docs/configuration/ai-assistants.md +0 -65
  51. package/docs/configuration/api-catalog.md +0 -109
  52. package/docs/configuration/authentication-auth0.md +0 -181
  53. package/docs/configuration/authentication-azure-ad.md +0 -245
  54. package/docs/configuration/authentication-clerk.md +0 -117
  55. package/docs/configuration/authentication-firebase.md +0 -69
  56. package/docs/configuration/authentication-pingfederate.md +0 -143
  57. package/docs/configuration/authentication-supabase.md +0 -233
  58. package/docs/configuration/build-configuration.mdx +0 -147
  59. package/docs/configuration/docs.md +0 -282
  60. package/docs/configuration/llms.md +0 -90
  61. package/docs/configuration/manifest.md +0 -129
  62. package/docs/configuration/navigation.mdx +0 -415
  63. package/docs/configuration/protected-routes.md +0 -150
  64. package/docs/configuration/search.md +0 -164
  65. package/docs/configuration/sentry.mdx +0 -44
  66. package/docs/configuration/site.md +0 -124
  67. package/docs/configuration/slots.mdx +0 -125
  68. package/docs/configuration/vite-config.md +0 -18
  69. package/docs/custom-plugins.md +0 -285
  70. package/docs/deploy/apache-nginx.md +0 -41
  71. package/docs/deploy/cloudflare-pages.md +0 -74
  72. package/docs/deploy/direct-upload.md +0 -17
  73. package/docs/deploy/github-pages.md +0 -49
  74. package/docs/deploy/vercel.md +0 -101
  75. package/docs/extending/events.md +0 -124
  76. package/docs/guides/environment-variables.md +0 -94
  77. package/docs/guides/managing-api-keys-and-identities.md +0 -89
  78. package/docs/guides/navigation-rules.mdx +0 -203
  79. package/docs/guides/processors.mdx +0 -234
  80. package/docs/guides/transforming-examples.md +0 -156
  81. package/docs/guides/using-multiple-apis.md +0 -87
  82. package/docs/plugins.md +0 -24
@@ -90,6 +90,7 @@ export class OpenIDAuthenticationProvider
90
90
  protected readonly redirectToAfterSignOut: string;
91
91
  private readonly audience?: string;
92
92
  private readonly scopes: string[];
93
+ private readonly tokenIdentity: boolean;
93
94
 
94
95
  constructor({
95
96
  issuer,
@@ -100,6 +101,7 @@ export class OpenIDAuthenticationProvider
100
101
  redirectToAfterSignOut = "/",
101
102
  basePath,
102
103
  scopes,
104
+ tokenIdentity = true,
103
105
  }: OpenIDAuthenticationConfig) {
104
106
  super();
105
107
  this.client = {
@@ -111,6 +113,7 @@ export class OpenIDAuthenticationProvider
111
113
  // This is the callback URL for the OAuth provider. So it needs the base path.
112
114
  this.callbackUrlPath = joinUrl(basePath, OPENID_CALLBACK_PATH);
113
115
  this.scopes = scopes ?? ["openid", "profile", "email"];
116
+ this.tokenIdentity = tokenIdentity;
114
117
 
115
118
  this.redirectToAfterSignUp = redirectToAfterSignUp;
116
119
  this.redirectToAfterSignIn = redirectToAfterSignIn;
@@ -366,7 +369,9 @@ export class OpenIDAuthenticationProvider
366
369
  * API behind the gateway's token check can be tried from the docs. A signed-out visitor gets no identity.
367
370
  */
368
371
  getIdentities = async (): Promise<ApiIdentity[]> => {
369
- if (!useAuthState.getState().isAuthenticated) return [];
372
+ // A gateway that checks keys, not tokens, answers the token with 401, so the option is not offered (API-129).
373
+ if (!this.tokenIdentity || !useAuthState.getState().isAuthenticated)
374
+ return [];
370
375
  return [
371
376
  {
372
377
  id: "signed-in-user",
@@ -1,82 +1,86 @@
1
- import type { MDXProps } from "mdx/types.js";
2
- import type { JSX } from "react";
3
- import type { ZudokuDocsConfig } from "../../../config/validators/ZudokuConfig.js";
4
- import type { Toc } from "../../../vite/mdx/rehype-extract-toc-with-jsx.js";
5
- import type { ApitogoPlugin } from "../../core/plugins.js";
6
-
7
- export interface MarkdownPluginOptions extends ZudokuDocsConfig {
8
- basePath: string;
9
- fileImports: Record<string, () => Promise<MDXImport>>;
10
- }
11
- export type MarkdownPluginDefaultOptions = Pick<
12
- Frontmatter,
13
- "toc" | "disablePager" | "showLastModified" | "suggestEdit" | "copyPage"
14
- >;
15
-
16
- export type Frontmatter = {
17
- title?: string;
18
- description?: string;
19
- category?: string;
20
- draft?: boolean;
21
- toc?: boolean;
22
- disablePager?: boolean;
23
- disable_pager?: boolean;
24
- showLastModified?: boolean;
25
- lastModifiedTime?: number;
26
- suggestEdit?: { url: string; text?: string } | false;
27
- copyPage?: boolean;
28
- };
29
-
30
- export type MDXImport = {
31
- tableOfContents: Toc;
32
- frontmatter: Frontmatter;
33
- excerpt?: string;
34
- __filepath: string;
35
- default: (props: MDXProps) => JSX.Element;
36
- };
37
-
38
- const INDEX_SUFFIX = "/index";
39
-
40
- // `index.mdx` is the page of its folder: `/index` also answers at `/`, `/guides/index` at `/guides`.
41
- // Without this the browser has no route for `/` (the pre-built index.html only hides that on first load).
42
- export const withIndexAliases = <T,>(
43
- fileImports: Record<string, T>,
44
- ): [string, T][] => {
45
- const entries = Object.entries(fileImports);
46
- return entries.flatMap(([routePath, value]): [string, T][] => {
47
- if (!routePath.endsWith(INDEX_SUFFIX)) return [[routePath, value]];
48
- const alias = routePath.slice(0, -INDEX_SUFFIX.length) || "/";
49
- return alias in fileImports
50
- ? [[routePath, value]]
51
- : [
52
- [routePath, value],
53
- [alias, value],
54
- ];
55
- });
56
- };
57
-
58
- export const markdownPlugin = (
59
- options: MarkdownPluginOptions,
60
- ): ApitogoPlugin => ({
61
- getRoutes: () => {
62
- return withIndexAliases(options.fileImports).map(
63
- ([routePath, importPromise]) => ({
64
- path: routePath,
65
- lazy: async () => {
66
- const { MdxPage } = await import("./MdxPage.js");
67
- const { default: Component, ...props } = await importPromise();
68
- return {
69
- element: (
70
- <MdxPage
71
- basePath={options.basePath}
72
- mdxComponent={Component}
73
- {...props}
74
- defaultOptions={options.defaultOptions}
75
- />
76
- ),
77
- };
78
- },
79
- }),
80
- );
81
- },
82
- });
1
+ import type { MDXProps } from "mdx/types.js";
2
+ import type { JSX } from "react";
3
+ import type { ZudokuDocsConfig } from "../../../config/validators/ZudokuConfig.js";
4
+ import type { Toc } from "../../../vite/mdx/rehype-extract-toc-with-jsx.js";
5
+ import type { ApitogoPlugin } from "../../core/plugins.js";
6
+
7
+ export interface MarkdownPluginOptions extends ZudokuDocsConfig {
8
+ basePath: string;
9
+ fileImports: Record<string, () => Promise<MDXImport>>;
10
+ /** Paths another page owns, such as the portal's home page at `/`: an index page keeps its own path but no alias there. */
11
+ reservedPaths?: string[];
12
+ }
13
+ export type MarkdownPluginDefaultOptions = Pick<
14
+ Frontmatter,
15
+ "toc" | "disablePager" | "showLastModified" | "suggestEdit" | "copyPage"
16
+ >;
17
+
18
+ export type Frontmatter = {
19
+ title?: string;
20
+ description?: string;
21
+ category?: string;
22
+ draft?: boolean;
23
+ toc?: boolean;
24
+ disablePager?: boolean;
25
+ disable_pager?: boolean;
26
+ showLastModified?: boolean;
27
+ lastModifiedTime?: number;
28
+ suggestEdit?: { url: string; text?: string } | false;
29
+ copyPage?: boolean;
30
+ };
31
+
32
+ export type MDXImport = {
33
+ tableOfContents: Toc;
34
+ frontmatter: Frontmatter;
35
+ excerpt?: string;
36
+ __filepath: string;
37
+ default: (props: MDXProps) => JSX.Element;
38
+ };
39
+
40
+ const INDEX_SUFFIX = "/index";
41
+
42
+ // `index.mdx` is the page of its folder: `/index` also answers at `/`, `/guides/index` at `/guides`.
43
+ // Without this the browser has no route for `/` (the pre-built index.html only hides that on first load).
44
+ // A path in `reserved` gets no alias: with a home page at `/`, `pages/index.md` took it over (API-129).
45
+ export const withIndexAliases = <T,>(
46
+ fileImports: Record<string, T>,
47
+ reserved: readonly string[] = [],
48
+ ): [string, T][] => {
49
+ const entries = Object.entries(fileImports);
50
+ return entries.flatMap(([routePath, value]): [string, T][] => {
51
+ if (!routePath.endsWith(INDEX_SUFFIX)) return [[routePath, value]];
52
+ const alias = routePath.slice(0, -INDEX_SUFFIX.length) || "/";
53
+ return alias in fileImports || reserved.includes(alias)
54
+ ? [[routePath, value]]
55
+ : [
56
+ [routePath, value],
57
+ [alias, value],
58
+ ];
59
+ });
60
+ };
61
+
62
+ export const markdownPlugin = (
63
+ options: MarkdownPluginOptions,
64
+ ): ApitogoPlugin => ({
65
+ getRoutes: () => {
66
+ return withIndexAliases(options.fileImports, options.reservedPaths).map(
67
+ ([routePath, importPromise]) => ({
68
+ path: routePath,
69
+ lazy: async () => {
70
+ const { MdxPage } = await import("./MdxPage.js");
71
+ const { default: Component, ...props } = await importPromise();
72
+ return {
73
+ element: (
74
+ <MdxPage
75
+ basePath={options.basePath}
76
+ mdxComponent={Component}
77
+ {...props}
78
+ defaultOptions={options.defaultOptions}
79
+ />
80
+ ),
81
+ };
82
+ },
83
+ }),
84
+ );
85
+ },
86
+ });
@@ -15,6 +15,7 @@ import { printDiagnosticsToConsole } from "../cli/common/output.js";
15
15
  import { findAvailablePort } from "../cli/common/utils/ports.js";
16
16
  import type { LoadedConfig } from "../config/config.js";
17
17
  import { loadZudokuConfig } from "../config/loader.js";
18
+ import type { ZudokuConfig } from "../config/validators/ZudokuConfig.js";
18
19
  import { createGraphQLServer } from "../lib/oas/graphql/index.js";
19
20
  import {
20
21
  getAppClientEntryPath,
@@ -22,6 +23,7 @@ import {
22
23
  getViteConfig,
23
24
  type ZudokuConfigEnv,
24
25
  } from "./config.js";
26
+ import { SSR_CONFIG_MODULE_ID, withSsrGraphParts } from "./dev-ssr-config.js";
25
27
  import { getDevHtml } from "./html.js";
26
28
  import { buildPagefindDevIndex } from "./pagefind-dev-index.js";
27
29
  import { ensurePagefindDevStub } from "./pagefind-dev-stub.js";
@@ -221,6 +223,10 @@ export class DevServer {
221
223
  getAppServerEntryPath(),
222
224
  );
223
225
 
226
+ const ssrConfig = await ssrEnvironment.runner.import<{
227
+ default: ZudokuConfig;
228
+ }>(SSR_CONFIG_MODULE_ID);
229
+
224
230
  const hasBody = req.method !== "GET" && req.method !== "HEAD";
225
231
  const request = new Request(
226
232
  `${this.protocol}://${req.headers.host}${url}`,
@@ -237,7 +243,9 @@ export class DevServer {
237
243
  const response = await entryServer.handleRequest({
238
244
  template,
239
245
  request,
240
- routes: entryServer.getRoutesByConfig(currentConfig),
246
+ routes: entryServer.getRoutesByConfig(
247
+ withSsrGraphParts(currentConfig, ssrConfig.default),
248
+ ),
241
249
  basePath: currentConfig.basePath,
242
250
  });
243
251
 
@@ -0,0 +1,32 @@
1
+ import type { ZudokuConfig } from "../config/validators/ZudokuConfig.js";
2
+
3
+ /** The config module the browser renders with; the dev server's SSR environment imports it too. */
4
+ export const SSR_CONFIG_MODULE_ID = "virtual:zudoku-config";
5
+
6
+ /** The config parts that hold modules of the config file's graph: plugin instances, custom-page elements, slots. */
7
+ type ConfigGraphParts = Pick<ZudokuConfig, "plugins" | "navigation" | "slots">;
8
+
9
+ /**
10
+ * The config the dev server renders a request with: the loaded config, with the plugins, navigation and slots of the
11
+ * SSR environment's own module graph.
12
+ *
13
+ * The loaded config is imported through Vite's `runnerImport`. Its module runner closes once the config file is
14
+ * evaluated and does not apply the React transform, but the config's plugins and components keep using it. A plugin
15
+ * page loaded with `lazy(() => import(...))`, such as the billing plugin's `/pricing`, then fails its server render
16
+ * with "Vite module runner has been closed", and a slot component (the portal footer) with "React is not defined".
17
+ * The browser falls back to client rendering after a hydration error. The SSR environment's copy of the config
18
+ * (`virtual:zudoku-config`, the module the browser renders with) imports the same file through the dev server's
19
+ * live runner and its plugins.
20
+ */
21
+ export function withSsrGraphParts<T extends ConfigGraphParts>(
22
+ config: T,
23
+ ssrConfig: ConfigGraphParts | undefined,
24
+ ): T {
25
+ if (!ssrConfig) return config;
26
+ return {
27
+ ...config,
28
+ ...(ssrConfig.plugins?.length ? { plugins: ssrConfig.plugins } : {}),
29
+ ...(ssrConfig.navigation ? { navigation: ssrConfig.navigation } : {}),
30
+ ...(ssrConfig.slots ? { slots: ssrConfig.slots } : {}),
31
+ };
32
+ }
@@ -139,12 +139,14 @@ import { runPluginTransformConfig } from "@lukoweb/apitogo/plugins";
139
139
 
140
140
  const embeddedConfig = ${serializeClientConfig(loadedConfig)};
141
141
 
142
- // JSON embedding drops Symbol values (React element's $$typeof). Prefer live
143
- // navigation/plugins from the TSX module so custom-page elements stay valid.
142
+ // JSON embedding drops Symbol values (React element's $typeof) and functions. Prefer live
143
+ // navigation/plugins/slots from the TSX module so custom-page elements and slot components (the
144
+ // portal footer, API-129) stay valid.
144
145
  const config = await runPluginTransformConfig({
145
146
  ...embeddedConfig,
146
147
  navigation: rawConfig?.navigation ?? embeddedConfig.navigation,
147
148
  plugins: rawConfig?.plugins ?? embeddedConfig.plugins ?? [],
149
+ slots: rawConfig?.slots ?? embeddedConfig.slots,
148
150
  __meta: ${JSON.stringify(clientMeta)},
149
151
  });
150
152
  export default config;