@lukoweb/apitogo 0.2.53 → 0.2.56
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 +15 -2
- package/dist/cli/cli.js +1852 -1235
- package/dist/cli/worker.js +7 -1
- package/dist/declarations/cli/common/spawn-command.d.ts +3 -0
- package/dist/declarations/cli/platform/client/platform-api.d.ts +23 -0
- package/dist/declarations/cli/platform/helpers/platform-plan-catalog.d.ts +6 -0
- package/dist/declarations/cli/platform/helpers/progress.d.ts +4 -1
- package/dist/declarations/lib/components/SiteTitle.d.ts +1 -0
- package/docs/cli/walkthrough.md +1 -1
- package/package.json +1 -1
- package/src/config/v2/docs-navigation.ts +11 -3
- package/src/config/v2/full-server.ts +1 -1
- package/src/config/v2/home-redirect.ts +39 -6
- package/src/config/v2/page-order.ts +73 -0
- package/src/config/v2/services.ts +17 -13
- package/src/config/v2/tsx-pages.ts +10 -1
- package/src/config/v2/wrapper.ts +26 -2
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +18 -1
- package/src/lib/components/SiteTitle.tsx +69 -58
- package/src/vite/prerender/worker.ts +132 -118
|
@@ -1,118 +1,132 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import Piscina from "piscina";
|
|
4
|
-
import { matchPath } from "react-router";
|
|
5
|
-
import { ProtectedRoutesSchema } from "../../config/validators/ProtectedRoutesSchema.js";
|
|
6
|
-
import type { ZudokuConfig } from "../../config/validators/ZudokuConfig.js";
|
|
7
|
-
import { runPluginTransformConfig } from "../../lib/core/transform-config.js";
|
|
8
|
-
import { joinUrl } from "../../lib/util/joinUrl.js";
|
|
9
|
-
import type { WorkerResult } from "./prerender.js";
|
|
10
|
-
|
|
11
|
-
type EntryServer = typeof import("../../app/entry.server.js");
|
|
12
|
-
|
|
13
|
-
export type StaticWorkerData = {
|
|
14
|
-
template: string;
|
|
15
|
-
distDir: string;
|
|
16
|
-
serverConfigPath: string;
|
|
17
|
-
entryServerPath: string;
|
|
18
|
-
writeRedirects: boolean;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type WorkerData = { urlPath: string };
|
|
22
|
-
|
|
23
|
-
const { template, distDir, serverConfigPath, entryServerPath, writeRedirects } =
|
|
24
|
-
Piscina.workerData as StaticWorkerData;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import Piscina from "piscina";
|
|
4
|
+
import { matchPath } from "react-router";
|
|
5
|
+
import { ProtectedRoutesSchema } from "../../config/validators/ProtectedRoutesSchema.js";
|
|
6
|
+
import type { ZudokuConfig } from "../../config/validators/ZudokuConfig.js";
|
|
7
|
+
import { runPluginTransformConfig } from "../../lib/core/transform-config.js";
|
|
8
|
+
import { joinUrl } from "../../lib/util/joinUrl.js";
|
|
9
|
+
import type { WorkerResult } from "./prerender.js";
|
|
10
|
+
|
|
11
|
+
type EntryServer = typeof import("../../app/entry.server.js");
|
|
12
|
+
|
|
13
|
+
export type StaticWorkerData = {
|
|
14
|
+
template: string;
|
|
15
|
+
distDir: string;
|
|
16
|
+
serverConfigPath: string;
|
|
17
|
+
entryServerPath: string;
|
|
18
|
+
writeRedirects: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type WorkerData = { urlPath: string };
|
|
22
|
+
|
|
23
|
+
const { template, distDir, serverConfigPath, entryServerPath, writeRedirects } =
|
|
24
|
+
Piscina.workerData as StaticWorkerData;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pages that redirect (the account pages before sign-in, for example) render a `<Navigate>`. In a static render it
|
|
28
|
+
* does nothing, and React Router says so on every build; the browser does the redirect when the page loads. The
|
|
29
|
+
* warning only worried tenants in the deploy output (API-126), so this one message is dropped.
|
|
30
|
+
*/
|
|
31
|
+
export const isStaticNavigateWarning = (args: unknown[]): boolean =>
|
|
32
|
+
typeof args[0] === "string" &&
|
|
33
|
+
args[0].includes("<Navigate> must not be used on the initial render");
|
|
34
|
+
// biome-ignore lint/suspicious/noConsole: this filters React Router's own console warning, it does not log.
|
|
35
|
+
const warn = console.warn.bind(console);
|
|
36
|
+
console.warn = (...args: unknown[]) => {
|
|
37
|
+
if (!isStaticNavigateWarning(args)) warn(...args);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const server: EntryServer = await import(entryServerPath);
|
|
41
|
+
const rawConfig: ZudokuConfig = await import(serverConfigPath).then(
|
|
42
|
+
(m) => m.default,
|
|
43
|
+
);
|
|
44
|
+
const config = await runPluginTransformConfig(rawConfig);
|
|
45
|
+
|
|
46
|
+
const routes = server.getRoutesByConfig(config);
|
|
47
|
+
const { basePath } = config;
|
|
48
|
+
|
|
49
|
+
const renderPage = async ({ urlPath }: WorkerData): Promise<WorkerResult> => {
|
|
50
|
+
const filename = urlPath === "/" ? "/index.html" : `${urlPath}.html`;
|
|
51
|
+
const pathname = joinUrl(basePath, urlPath);
|
|
52
|
+
const url = joinUrl("http://localhost", pathname);
|
|
53
|
+
const outputPath = path.join(distDir, filename);
|
|
54
|
+
|
|
55
|
+
const request = new Request(url);
|
|
56
|
+
|
|
57
|
+
const protectedRoutes = ProtectedRoutesSchema.parse(config.protectedRoutes);
|
|
58
|
+
const isProtectedRoute = protectedRoutes
|
|
59
|
+
? Object.keys(protectedRoutes).some((route) =>
|
|
60
|
+
matchPath({ path: route, end: true }, urlPath),
|
|
61
|
+
)
|
|
62
|
+
: false;
|
|
63
|
+
|
|
64
|
+
// Get the main response
|
|
65
|
+
const response = await server.handleRequest({
|
|
66
|
+
template,
|
|
67
|
+
request,
|
|
68
|
+
routes,
|
|
69
|
+
basePath,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Check for redirects
|
|
73
|
+
if ([301, 302, 303, 307, 308].includes(response.status)) {
|
|
74
|
+
const redirectTo = response.headers.get("Location") ?? "";
|
|
75
|
+
|
|
76
|
+
if (writeRedirects) {
|
|
77
|
+
const redirectHtml = `<!doctype html><script>window.location.href=${JSON.stringify(redirectTo)};</script>`;
|
|
78
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
79
|
+
await fs.writeFile(outputPath, redirectHtml);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
outputPath,
|
|
84
|
+
redirect: { from: pathname, to: redirectTo },
|
|
85
|
+
statusCode: response.status,
|
|
86
|
+
html: "",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (response.status >= 500) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`SSR failed with status ${response.status} for path: ${urlPath}`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Get HTML content for file write
|
|
97
|
+
const fileContent = response.body ? await response.text() : "";
|
|
98
|
+
|
|
99
|
+
// For protected routes, do a second render with protection bypassed for search index
|
|
100
|
+
let html = fileContent;
|
|
101
|
+
if (isProtectedRoute) {
|
|
102
|
+
const bypassRequest = new Request(url);
|
|
103
|
+
const bypassResponse = await server.handleRequest({
|
|
104
|
+
template,
|
|
105
|
+
request: bypassRequest,
|
|
106
|
+
routes,
|
|
107
|
+
basePath,
|
|
108
|
+
bypassProtection: true,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (bypassResponse.status >= 500) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`SSR failed (bypass render) with status ${bypassResponse.status} for path: ${urlPath}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
html = bypassResponse.body ? await bypassResponse.text() : "";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Write the file
|
|
121
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
122
|
+
await fs.writeFile(outputPath, fileContent);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
outputPath,
|
|
126
|
+
statusCode: response.status,
|
|
127
|
+
redirect: undefined,
|
|
128
|
+
html,
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default renderPage;
|