@lukoweb/apitogo 0.1.50 → 0.1.51
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/dist/cli/cli.js +1124 -892
- package/dist/declarations/config/apitogo-config-loader.d.ts +7 -0
- package/dist/declarations/config/build-apitogo-config.d.ts +7 -0
- package/dist/declarations/config/loader.d.ts +2 -0
- package/dist/declarations/config/local-manifest.d.ts +8 -1
- package/dist/declarations/config/validators/HeaderNavigationSchema.d.ts +24 -24
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +9 -9
- package/dist/declarations/index.d.ts +2 -1
- package/dist/declarations/lib/authentication/auth-header-nav.d.ts +10 -0
- package/dist/declarations/lib/ui/Button.d.ts +1 -1
- package/dist/declarations/lib/ui/Item.d.ts +1 -1
- package/docs/configuration/authentication.md +6 -5
- package/docs/configuration/manifest.md +60 -71
- package/package.json +17 -1
- package/src/config/apitogo-config-loader.ts +224 -0
- package/src/config/build-apitogo-config.ts +43 -0
- package/src/config/loader.ts +40 -1
- package/src/config/local-manifest.ts +60 -8
- package/src/index.ts +8 -1
- package/src/lib/authentication/auth-header-nav.ts +56 -0
- package/src/lib/components/Header.tsx +48 -60
- package/src/lib/components/MobileTopNavigation.tsx +29 -40
|
@@ -1,68 +1,50 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Site configuration (apitogo.config.json)
|
|
3
3
|
sidebar_icon: file-json
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Dev portal sites
|
|
7
|
-
paths, and gateway
|
|
8
|
-
|
|
6
|
+
Dev portal sites use a single JSON configuration file for site settings, plans,
|
|
7
|
+
branding, backend paths, and gateway metadata. Code-only pieces (plugins, custom
|
|
8
|
+
components) stay in `apitogo.config.tsx`.
|
|
9
9
|
|
|
10
10
|
## Files
|
|
11
11
|
|
|
12
|
-
| File
|
|
13
|
-
|
|
|
14
|
-
| `apitogo.json`
|
|
15
|
-
| `apitogo.
|
|
16
|
-
| `
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
New scaffolds ship `apitogo.json` plus `apitogo.local.example.json` and `apitogo.prod.example.json`
|
|
20
|
-
to show how overrides work. Copy or rename the examples when you need environment-specific values.
|
|
21
|
-
|
|
22
|
-
## Resolution
|
|
23
|
-
|
|
24
|
-
At dev and build time the tooling merges the base file with the environment override:
|
|
25
|
-
|
|
26
|
-
```mermaid
|
|
27
|
-
flowchart LR
|
|
28
|
-
base["apitogo.json"]
|
|
29
|
-
local["apitogo.local.json"]
|
|
30
|
-
prod["apitogo.prod.json"]
|
|
31
|
-
devMerge["merge"]
|
|
32
|
-
buildMerge["merge"]
|
|
33
|
-
viteDev["npm run dev"]
|
|
34
|
-
viteBuild["npm run build"]
|
|
35
|
-
|
|
36
|
-
base --> devMerge
|
|
37
|
-
local --> devMerge
|
|
38
|
-
base --> buildMerge
|
|
39
|
-
prod --> buildMerge
|
|
40
|
-
viteDev --> devMerge
|
|
41
|
-
buildMerge --> viteBuild
|
|
42
|
-
```
|
|
12
|
+
| File | Purpose |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `apitogo.config.json` | Committed defaults: site, modules, plans, branding, backend, gateway |
|
|
15
|
+
| `apitogo.config.tsx` | Plugins and other non-JSON configuration |
|
|
16
|
+
| `.env` / `.env.local` / `.env.production` | Per-environment overrides via `APITOGO_CONFIG_*` and `VITE_*` vars |
|
|
17
|
+
|
|
18
|
+
## Environment overrides
|
|
43
19
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
20
|
+
Use `APITOGO_CONFIG_<path>` with double underscores for nested paths:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
APITOGO_CONFIG_site__title=My Local Site
|
|
24
|
+
APITOGO_CONFIG_pricing__enabled=true
|
|
25
|
+
APITOGO_CONFIG_projectId=your-local-project-id
|
|
26
|
+
APITOGO_CONFIG_backend__sourceDirectory=../my-api
|
|
27
|
+
```
|
|
48
28
|
|
|
49
|
-
|
|
50
|
-
manifest (legacy single-file projects).
|
|
29
|
+
Values are parsed as JSON when possible (`true`, `false`, numbers, objects).
|
|
51
30
|
|
|
52
|
-
|
|
31
|
+
For the live dev-portal API URL (client-visible):
|
|
53
32
|
|
|
54
|
-
|
|
33
|
+
```sh
|
|
34
|
+
VITE_APITOGO_DEV_PORTAL_API_URL=https://your-dev-portal-api.example.com
|
|
35
|
+
```
|
|
55
36
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
- `plans`: merged by `sku` — an override plan with the same SKU updates only the fields you specify
|
|
37
|
+
Keep secrets (OIDC client secret, Stripe keys, etc.) in `.env.local` using the
|
|
38
|
+
same `APITOGO_CONFIG_*` pattern — never commit them.
|
|
59
39
|
|
|
60
|
-
Example
|
|
40
|
+
## Example `apitogo.config.json`
|
|
61
41
|
|
|
62
42
|
```json
|
|
63
43
|
{
|
|
64
44
|
"siteName": "My API",
|
|
65
45
|
"branding": { "title": "My API" },
|
|
46
|
+
"pricing": { "enabled": true },
|
|
47
|
+
"authentication": { "enabled": true, "type": "dev-portal" },
|
|
66
48
|
"plans": [
|
|
67
49
|
{
|
|
68
50
|
"sku": "pro",
|
|
@@ -71,45 +53,52 @@ Example base (`apitogo.json`):
|
|
|
71
53
|
"priceAmount": 9.99,
|
|
72
54
|
"billingPeriod": "month"
|
|
73
55
|
}
|
|
74
|
-
]
|
|
56
|
+
],
|
|
57
|
+
"site": {
|
|
58
|
+
"title": "My API",
|
|
59
|
+
"logo": {
|
|
60
|
+
"src": { "light": "/logo-light.png", "dark": "/logo-dark.png" },
|
|
61
|
+
"alt": "My API",
|
|
62
|
+
"width": "130px"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"docs": { "files": "/pages/**/*.{md,mdx}" }
|
|
75
66
|
}
|
|
76
67
|
```
|
|
77
68
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```json
|
|
81
|
-
{
|
|
82
|
-
"projectId": "your-local-project-id",
|
|
83
|
-
"backend": { "sourceDirectory": "../my-api" }
|
|
84
|
-
}
|
|
85
|
-
```
|
|
69
|
+
## Thin `apitogo.config.tsx`
|
|
86
70
|
|
|
87
|
-
|
|
71
|
+
```tsx
|
|
72
|
+
import { buildApitogoConfig } from "@lukoweb/apitogo";
|
|
73
|
+
import { devPortalBillingPlugin } from "@lukoweb/apitogo-plugin-dev-portal-billing";
|
|
88
74
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"backend": { "deployed": true }
|
|
93
|
-
}
|
|
75
|
+
export default buildApitogoConfig({
|
|
76
|
+
plugins: [devPortalBillingPlugin()],
|
|
77
|
+
});
|
|
94
78
|
```
|
|
95
79
|
|
|
96
80
|
## What to put where
|
|
97
81
|
|
|
98
|
-
| Concern
|
|
99
|
-
|
|
|
100
|
-
| Plans, rate limits, `includedActionKeys`
|
|
101
|
-
| Branding, site name
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
| OIDC / Stripe | `apitogo.local.secrets.json` (see [Authentication](./authentication.md)) |
|
|
82
|
+
| Concern | Recommended location |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| Plans, rate limits, `includedActionKeys` | `apitogo.config.json` |
|
|
85
|
+
| Branding, site name, modules, navigation | `apitogo.config.json` |
|
|
86
|
+
| Machine-specific paths, `projectId` | `.env.local` via `APITOGO_CONFIG_*` |
|
|
87
|
+
| OIDC / Stripe for publish | `.env.local` via `APITOGO_CONFIG_*` |
|
|
88
|
+
| Plugins | `apitogo.config.tsx` |
|
|
106
89
|
|
|
107
90
|
## Hot reload
|
|
108
91
|
|
|
109
|
-
During `npm run dev`, changes to `apitogo.
|
|
92
|
+
During `npm run dev`, changes to `apitogo.config.json` hot-reload on `/pricing`
|
|
110
93
|
without restarting the server. Restart the dev server after changing
|
|
111
94
|
`VITE_APITOGO_DEV_PORTAL_API_URL` in `.env`.
|
|
112
95
|
|
|
96
|
+
## Legacy manifests
|
|
97
|
+
|
|
98
|
+
Older projects may still have `apitogo.json` with optional
|
|
99
|
+
`apitogo.local.json` / `apitogo.prod.json` overrides. The tooling falls back to
|
|
100
|
+
those files when `apitogo.config.json` is missing.
|
|
101
|
+
|
|
113
102
|
## Related configuration
|
|
114
103
|
|
|
115
104
|
- Site navigation, docs, and plugins: [`apitogo.config.tsx`](./overview.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukoweb/apitogo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css",
|
|
@@ -153,6 +153,14 @@
|
|
|
153
153
|
"./local-manifest": {
|
|
154
154
|
"types": "./dist/declarations/config/local-manifest.d.ts",
|
|
155
155
|
"default": "./src/config/local-manifest.ts"
|
|
156
|
+
},
|
|
157
|
+
"./build-config": {
|
|
158
|
+
"types": "./dist/declarations/config/build-apitogo-config.d.ts",
|
|
159
|
+
"default": "./src/config/build-apitogo-config.ts"
|
|
160
|
+
},
|
|
161
|
+
"./config-loader": {
|
|
162
|
+
"types": "./dist/declarations/config/apitogo-config-loader.d.ts",
|
|
163
|
+
"default": "./src/config/apitogo-config-loader.ts"
|
|
156
164
|
}
|
|
157
165
|
},
|
|
158
166
|
"scripts": {
|
|
@@ -467,6 +475,14 @@
|
|
|
467
475
|
"./local-manifest": {
|
|
468
476
|
"types": "./dist/declarations/config/local-manifest.d.ts",
|
|
469
477
|
"default": "./src/config/local-manifest.ts"
|
|
478
|
+
},
|
|
479
|
+
"./build-config": {
|
|
480
|
+
"types": "./dist/declarations/config/build-apitogo-config.d.ts",
|
|
481
|
+
"default": "./src/config/build-apitogo-config.ts"
|
|
482
|
+
},
|
|
483
|
+
"./config-loader": {
|
|
484
|
+
"types": "./dist/declarations/config/apitogo-config-loader.d.ts",
|
|
485
|
+
"default": "./src/config/apitogo-config-loader.ts"
|
|
470
486
|
}
|
|
471
487
|
},
|
|
472
488
|
"access": "public"
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const APITOGO_CONFIG_FILENAME = "apitogo.config.json";
|
|
5
|
+
|
|
6
|
+
const ENV_PREFIX = "APITOGO_CONFIG_";
|
|
7
|
+
|
|
8
|
+
const MANIFEST_KEYS = [
|
|
9
|
+
"siteName",
|
|
10
|
+
"branding",
|
|
11
|
+
"pricing",
|
|
12
|
+
"authentication",
|
|
13
|
+
"plans",
|
|
14
|
+
"backend",
|
|
15
|
+
"gateway",
|
|
16
|
+
"projectId",
|
|
17
|
+
"projectName",
|
|
18
|
+
] as const;
|
|
19
|
+
|
|
20
|
+
const SITE_CONFIG_KEYS = [
|
|
21
|
+
"site",
|
|
22
|
+
"metadata",
|
|
23
|
+
"docs",
|
|
24
|
+
"modules",
|
|
25
|
+
"navigation",
|
|
26
|
+
"apis",
|
|
27
|
+
"redirects",
|
|
28
|
+
"theme",
|
|
29
|
+
"footer",
|
|
30
|
+
"search",
|
|
31
|
+
"llms",
|
|
32
|
+
"protectedRoutes",
|
|
33
|
+
"slots",
|
|
34
|
+
"mdx",
|
|
35
|
+
"apiKeys",
|
|
36
|
+
"catalog",
|
|
37
|
+
"versions",
|
|
38
|
+
"options",
|
|
39
|
+
] as const;
|
|
40
|
+
|
|
41
|
+
export type ApitogoJsonConfig = Record<string, unknown>;
|
|
42
|
+
|
|
43
|
+
function parseEnvValue(raw: string | undefined): unknown {
|
|
44
|
+
if (raw === undefined) {
|
|
45
|
+
return raw;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const trimmed = raw.trim();
|
|
49
|
+
if (trimmed === "true") {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (trimmed === "false") {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (trimmed === "null") {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
60
|
+
return Number(trimmed);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
(trimmed.startsWith("{") && trimmed.endsWith("}")) ||
|
|
65
|
+
(trimmed.startsWith("[") && trimmed.endsWith("]"))
|
|
66
|
+
) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(trimmed) as unknown;
|
|
69
|
+
} catch {
|
|
70
|
+
return trimmed;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return trimmed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function setNestedValue(
|
|
78
|
+
target: Record<string, unknown>,
|
|
79
|
+
pathParts: string[],
|
|
80
|
+
value: unknown,
|
|
81
|
+
): void {
|
|
82
|
+
if (pathParts.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let current: Record<string, unknown> = target;
|
|
87
|
+
for (let index = 0; index < pathParts.length - 1; index += 1) {
|
|
88
|
+
const key = pathParts[index];
|
|
89
|
+
if (!key) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const next = current[key];
|
|
93
|
+
if (
|
|
94
|
+
next === undefined ||
|
|
95
|
+
next === null ||
|
|
96
|
+
typeof next !== "object" ||
|
|
97
|
+
Array.isArray(next)
|
|
98
|
+
) {
|
|
99
|
+
current[key] = {};
|
|
100
|
+
}
|
|
101
|
+
current = current[key] as Record<string, unknown>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const leafKey = pathParts[pathParts.length - 1];
|
|
105
|
+
if (!leafKey) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
current[leafKey] = value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function applyEnvOverrides(
|
|
113
|
+
config: ApitogoJsonConfig,
|
|
114
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
115
|
+
): ApitogoJsonConfig {
|
|
116
|
+
const result = structuredClone(config);
|
|
117
|
+
|
|
118
|
+
for (const [key, value] of Object.entries(env)) {
|
|
119
|
+
if (!key.startsWith(ENV_PREFIX) || value === undefined) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const pathParts = key
|
|
124
|
+
.slice(ENV_PREFIX.length)
|
|
125
|
+
.split("__")
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
|
|
128
|
+
if (pathParts.length === 0) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
setNestedValue(result, pathParts, parseEnvValue(value));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function loadApitogoConfig(rootDir = process.cwd()): ApitogoJsonConfig {
|
|
139
|
+
const configPath = path.join(path.resolve(rootDir), APITOGO_CONFIG_FILENAME);
|
|
140
|
+
|
|
141
|
+
if (!existsSync(configPath)) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Missing ${APITOGO_CONFIG_FILENAME} in ${path.resolve(rootDir)}`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const raw = readFileSync(configPath, "utf8");
|
|
148
|
+
const parsed = JSON.parse(raw) as ApitogoJsonConfig;
|
|
149
|
+
return applyEnvOverrides(parsed, process.env);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function extractManifest(
|
|
153
|
+
config: ApitogoJsonConfig | null | undefined,
|
|
154
|
+
): ApitogoJsonConfig | null {
|
|
155
|
+
if (!config || typeof config !== "object") {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const manifest: ApitogoJsonConfig = {};
|
|
160
|
+
for (const key of MANIFEST_KEYS) {
|
|
161
|
+
if (config[key] !== undefined) {
|
|
162
|
+
manifest[key] = config[key];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const authentication = manifest.authentication;
|
|
167
|
+
if (authentication && typeof authentication === "object") {
|
|
168
|
+
const { enabled } = authentication as { enabled?: boolean };
|
|
169
|
+
manifest.authentication =
|
|
170
|
+
enabled === undefined ? undefined : { enabled };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return Object.keys(manifest).length > 0 ? manifest : null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function extractSiteConfig(
|
|
177
|
+
config: ApitogoJsonConfig | null | undefined,
|
|
178
|
+
): ApitogoJsonConfig {
|
|
179
|
+
if (!config || typeof config !== "object") {
|
|
180
|
+
return {};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const siteConfig: ApitogoJsonConfig = {};
|
|
184
|
+
for (const key of SITE_CONFIG_KEYS) {
|
|
185
|
+
if (config[key] !== undefined) {
|
|
186
|
+
siteConfig[key] = config[key];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const auth = config.authentication;
|
|
191
|
+
if (auth && typeof auth === "object") {
|
|
192
|
+
const authRecord = auth as Record<string, unknown>;
|
|
193
|
+
if (authRecord.type) {
|
|
194
|
+
const { enabled: _enabled, ...authConfig } = authRecord;
|
|
195
|
+
siteConfig.authentication = authConfig;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const site = siteConfig.site;
|
|
200
|
+
const branding = config.branding;
|
|
201
|
+
const title =
|
|
202
|
+
(site &&
|
|
203
|
+
typeof site === "object" &&
|
|
204
|
+
(site as Record<string, unknown>).title) ??
|
|
205
|
+
(branding &&
|
|
206
|
+
typeof branding === "object" &&
|
|
207
|
+
(branding as Record<string, unknown>).title) ??
|
|
208
|
+
config.siteName;
|
|
209
|
+
|
|
210
|
+
if (title) {
|
|
211
|
+
siteConfig.site = {
|
|
212
|
+
...(site && typeof site === "object"
|
|
213
|
+
? (site as Record<string, unknown>)
|
|
214
|
+
: {}),
|
|
215
|
+
title,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return siteConfig;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function apitogoConfigPath(rootDir: string): string {
|
|
223
|
+
return path.join(path.resolve(rootDir), APITOGO_CONFIG_FILENAME);
|
|
224
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ApitogoConfig } from "./config.js";
|
|
2
|
+
import {
|
|
3
|
+
extractSiteConfig,
|
|
4
|
+
loadApitogoConfig,
|
|
5
|
+
} from "./apitogo-config-loader.js";
|
|
6
|
+
|
|
7
|
+
type BuildApitogoConfigOptions = {
|
|
8
|
+
rootDir?: string;
|
|
9
|
+
overrides?: Partial<ApitogoConfig>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function buildApitogoConfig({
|
|
13
|
+
rootDir,
|
|
14
|
+
overrides = {},
|
|
15
|
+
}: BuildApitogoConfigOptions = {}): ApitogoConfig {
|
|
16
|
+
const jsonConfig = loadApitogoConfig(rootDir);
|
|
17
|
+
const siteConfig = extractSiteConfig(jsonConfig);
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
...siteConfig,
|
|
21
|
+
...overrides,
|
|
22
|
+
authentication: {
|
|
23
|
+
...(siteConfig.authentication as ApitogoConfig["authentication"]),
|
|
24
|
+
...overrides.authentication,
|
|
25
|
+
},
|
|
26
|
+
site: {
|
|
27
|
+
...(siteConfig.site as ApitogoConfig["site"]),
|
|
28
|
+
...overrides.site,
|
|
29
|
+
},
|
|
30
|
+
metadata: {
|
|
31
|
+
...(siteConfig.metadata as ApitogoConfig["metadata"]),
|
|
32
|
+
...overrides.metadata,
|
|
33
|
+
},
|
|
34
|
+
docs: {
|
|
35
|
+
...(siteConfig.docs as ApitogoConfig["docs"]),
|
|
36
|
+
...overrides.docs,
|
|
37
|
+
},
|
|
38
|
+
modules: {
|
|
39
|
+
...(siteConfig.modules as ApitogoConfig["modules"]),
|
|
40
|
+
...overrides.modules,
|
|
41
|
+
},
|
|
42
|
+
} as ApitogoConfig;
|
|
43
|
+
}
|
package/src/config/loader.ts
CHANGED
|
@@ -10,9 +10,17 @@ import {
|
|
|
10
10
|
} from "vite";
|
|
11
11
|
import { logger } from "../cli/common/logger.js";
|
|
12
12
|
import { getZudokuRootDir } from "../cli/common/package-json.js";
|
|
13
|
+
import { applyAuthenticationHeaderNav } from "../lib/authentication/auth-header-nav.js";
|
|
13
14
|
import { runPluginTransformConfig } from "../lib/core/transform-config.js";
|
|
14
15
|
import invariant from "../lib/util/invariant.js";
|
|
15
16
|
import { fileExists } from "./file-exists.js";
|
|
17
|
+
import {
|
|
18
|
+
isAuthenticationEnabled,
|
|
19
|
+
isPricingEnabled,
|
|
20
|
+
manifestPathsForEnv,
|
|
21
|
+
readEffectiveManifest,
|
|
22
|
+
resolveManifestEnv,
|
|
23
|
+
} from "./local-manifest.js";
|
|
16
24
|
import { resolveModulesConfig } from "./resolve-modules.js";
|
|
17
25
|
import type { ResolvedModulesConfig } from "./validators/ModulesSchema.js";
|
|
18
26
|
import type { ZudokuConfig } from "./validators/ZudokuConfig.js";
|
|
@@ -25,6 +33,8 @@ export type ConfigWithMeta = ZudokuConfig & {
|
|
|
25
33
|
configPath: string;
|
|
26
34
|
mode: typeof process.env.ZUDOKU_ENV;
|
|
27
35
|
dependencies: string[];
|
|
36
|
+
pricingEnabled?: boolean;
|
|
37
|
+
authenticationEnabled?: boolean;
|
|
28
38
|
};
|
|
29
39
|
__resolvedModules: ResolvedModulesConfig;
|
|
30
40
|
};
|
|
@@ -157,8 +167,17 @@ function isWatchableConfigDependency(depPath: string): boolean {
|
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
function getWatchableConfigDependencies(config: ConfigWithMeta): string[] {
|
|
170
|
+
const manifestEnv = resolveManifestEnv(
|
|
171
|
+
config.__meta.mode === "production" ? "production" : "development",
|
|
172
|
+
);
|
|
173
|
+
const { watchPaths } = manifestPathsForEnv(
|
|
174
|
+
config.__meta.rootDir,
|
|
175
|
+
manifestEnv,
|
|
176
|
+
);
|
|
177
|
+
|
|
160
178
|
return [
|
|
161
179
|
config.__meta.configPath,
|
|
180
|
+
...watchPaths,
|
|
162
181
|
...config.__meta.dependencies.filter(isWatchableConfigDependency),
|
|
163
182
|
];
|
|
164
183
|
}
|
|
@@ -171,6 +190,10 @@ async function hasConfigChanged() {
|
|
|
171
190
|
try {
|
|
172
191
|
const hasChanged = await Promise.all(
|
|
173
192
|
files.map(async (depPath) => {
|
|
193
|
+
if (!(await fileExists(depPath))) {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
174
197
|
const depStat = await stat(depPath);
|
|
175
198
|
const cachedMtime = modifiedTimes?.get(depPath);
|
|
176
199
|
return !cachedMtime || depStat.mtimeMs !== cachedMtime;
|
|
@@ -192,6 +215,10 @@ async function updateModifiedTimes() {
|
|
|
192
215
|
|
|
193
216
|
await Promise.all(
|
|
194
217
|
files.map(async (depPath) => {
|
|
218
|
+
if (!(await fileExists(depPath))) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
195
222
|
const depStat = await stat(depPath);
|
|
196
223
|
modifiedTimes?.set(depPath, depStat.mtimeMs);
|
|
197
224
|
}),
|
|
@@ -225,7 +252,19 @@ export async function loadZudokuConfig(
|
|
|
225
252
|
|
|
226
253
|
try {
|
|
227
254
|
const loadedConfig = await loadZudokuConfigWithMeta(rootDir);
|
|
228
|
-
const
|
|
255
|
+
const manifestEnv = resolveManifestEnv(configEnv.mode);
|
|
256
|
+
const manifest = await readEffectiveManifest(rootDir, manifestEnv);
|
|
257
|
+
const configWithManifestMeta: ConfigWithMeta = {
|
|
258
|
+
...loadedConfig,
|
|
259
|
+
__meta: {
|
|
260
|
+
...loadedConfig.__meta,
|
|
261
|
+
pricingEnabled: isPricingEnabled(manifest),
|
|
262
|
+
authenticationEnabled: isAuthenticationEnabled(manifest),
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
const transformedConfig = applyAuthenticationHeaderNav(
|
|
266
|
+
await runPluginTransformConfig(configWithManifestMeta),
|
|
267
|
+
);
|
|
229
268
|
config = {
|
|
230
269
|
...transformedConfig,
|
|
231
270
|
__resolvedModules: resolveModulesConfig(transformedConfig),
|