@lukoweb/apitogo 0.2.56 → 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 (80) hide show
  1. package/dist/cli/cli.js +1026 -540
  2. package/dist/declarations/cli/platform/client/platform-api.d.ts +6 -0
  3. package/dist/declarations/config/validators/ZudokuConfig.d.ts +2 -0
  4. package/dist/declarations/lib/authentication/providers/openid.d.ts +2 -1
  5. package/dist/declarations/lib/plugins/markdown/index.d.ts +2 -1
  6. package/dist/flat-config.d.ts +1 -0
  7. package/docs/cli/commands.md +50 -32
  8. package/docs/cli/go-live.md +66 -93
  9. package/docs/cli/overview.md +18 -17
  10. package/docs/cli/setup.md +130 -141
  11. package/docs/cli/shape.md +94 -11
  12. package/docs/components/slot.mdx +116 -119
  13. package/docs/configuration/api-reference.md +98 -397
  14. package/docs/configuration/authentication.md +118 -159
  15. package/docs/configuration/footer.mdx +89 -118
  16. package/docs/configuration/make-config.mdx +2 -2
  17. package/docs/configuration/overview.md +97 -166
  18. package/docs/configuration/plans.md +100 -110
  19. package/docs/configuration/portal.md +116 -0
  20. package/docs/customization/colors-theme.mdx +255 -278
  21. package/docs/customization/fonts.md +105 -110
  22. package/docs/customization/landing-page.md +189 -188
  23. package/docs/deploy/apitogo.md +13 -17
  24. package/docs/deployment.md +15 -9
  25. package/docs/guides/stripe-setup.mdx +123 -0
  26. package/docs/markdown/frontmatter.md +172 -173
  27. package/docs/mcp/go-live.md +100 -100
  28. package/docs/mcp/overview.md +76 -76
  29. package/docs/quickstart.md +88 -72
  30. package/docs/writing.mdx +68 -72
  31. package/package.json +1 -1
  32. package/src/config/v2/docs-navigation.ts +7 -2
  33. package/src/config/v2/index.ts +1 -0
  34. package/src/config/v2/resolve.ts +28 -0
  35. package/src/config/v2/rules.ts +32 -1
  36. package/src/config/v2/schema.ts +9 -0
  37. package/src/config/v2/services.ts +78 -2
  38. package/src/config/v2/starter-landing.ts +41 -0
  39. package/src/config/v2/wrapper.ts +127 -7
  40. package/src/config/validators/ZudokuConfig.ts +794 -792
  41. package/src/lib/authentication/providers/openid.tsx +6 -1
  42. package/src/lib/plugins/markdown/index.tsx +86 -82
  43. package/src/vite/dev-server.ts +9 -1
  44. package/src/vite/dev-ssr-config.ts +32 -0
  45. package/src/vite/plugin-config.ts +4 -2
  46. package/src/vite/plugin-docs.ts +213 -205
  47. package/docs/concepts/auth-provider-api-identities.md +0 -105
  48. package/docs/configuration/ai-assistants.md +0 -65
  49. package/docs/configuration/api-catalog.md +0 -109
  50. package/docs/configuration/authentication-auth0.md +0 -181
  51. package/docs/configuration/authentication-azure-ad.md +0 -245
  52. package/docs/configuration/authentication-clerk.md +0 -117
  53. package/docs/configuration/authentication-firebase.md +0 -69
  54. package/docs/configuration/authentication-pingfederate.md +0 -143
  55. package/docs/configuration/authentication-supabase.md +0 -233
  56. package/docs/configuration/build-configuration.mdx +0 -147
  57. package/docs/configuration/docs.md +0 -282
  58. package/docs/configuration/llms.md +0 -90
  59. package/docs/configuration/manifest.md +0 -129
  60. package/docs/configuration/navigation.mdx +0 -415
  61. package/docs/configuration/protected-routes.md +0 -150
  62. package/docs/configuration/search.md +0 -164
  63. package/docs/configuration/sentry.mdx +0 -44
  64. package/docs/configuration/site.md +0 -124
  65. package/docs/configuration/slots.mdx +0 -125
  66. package/docs/configuration/vite-config.md +0 -18
  67. package/docs/custom-plugins.md +0 -285
  68. package/docs/deploy/apache-nginx.md +0 -41
  69. package/docs/deploy/cloudflare-pages.md +0 -74
  70. package/docs/deploy/direct-upload.md +0 -17
  71. package/docs/deploy/github-pages.md +0 -49
  72. package/docs/deploy/vercel.md +0 -101
  73. package/docs/extending/events.md +0 -124
  74. package/docs/guides/environment-variables.md +0 -94
  75. package/docs/guides/managing-api-keys-and-identities.md +0 -89
  76. package/docs/guides/navigation-rules.mdx +0 -203
  77. package/docs/guides/processors.mdx +0 -234
  78. package/docs/guides/transforming-examples.md +0 -156
  79. package/docs/guides/using-multiple-apis.md +0 -87
  80. 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;