@lukoweb/apitogo 0.1.51 → 0.1.53
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 +381 -196
- package/dist/declarations/config/apitogo-config-core.d.ts +25 -0
- package/dist/declarations/config/apitogo-config-loader.d.ts +2 -7
- package/dist/declarations/config/apitogo-config-loader.node.d.ts +4 -0
- package/dist/declarations/config/build-apitogo-config.d.ts +5 -4
- package/dist/declarations/config/loader.d.ts +2 -0
- package/dist/declarations/config/local-manifest.d.ts +8 -8
- package/dist/declarations/config/validators/ModulesSchema.d.ts +38 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +9 -0
- package/dist/declarations/index.d.ts +1 -1
- package/dist/declarations/lib/core/plugins.d.ts +1 -0
- package/dist/flat-config.d.ts +36 -23
- package/docs/configuration/authentication.md +11 -9
- package/docs/configuration/manifest.md +75 -74
- package/package.json +1 -1
- package/src/config/apitogo-config-core.ts +485 -0
- package/src/config/apitogo-config-loader.node.ts +68 -0
- package/src/config/apitogo-config-loader.ts +6 -224
- package/src/config/build-apitogo-config.ts +26 -17
- package/src/config/loader.ts +23 -2
- package/src/config/local-manifest.ts +27 -59
- package/src/config/resolve-modules.ts +16 -13
- package/src/config/validators/ModulesSchema.ts +17 -0
- package/src/index.ts +3 -2
- package/src/lib/authentication/providers/dev-portal-utils.ts +7 -2
- package/src/lib/core/plugins.ts +1 -0
- package/src/vite/dev-portal-env.ts +12 -4
- package/src/vite/plugin-config.ts +20 -3
- package/src/vite/plugin-local-manifest.ts +15 -0
|
@@ -1,224 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
}
|
|
1
|
+
export * from "./apitogo-config-core.js";
|
|
2
|
+
export {
|
|
3
|
+
apitogoConfigPath,
|
|
4
|
+
loadApitogoConfig,
|
|
5
|
+
readAuthenticationSecrets,
|
|
6
|
+
} from "./apitogo-config-loader.node.js";
|
|
@@ -1,43 +1,52 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ApitogoPlugin } from "../lib/core/plugins.js";
|
|
2
2
|
import {
|
|
3
3
|
extractSiteConfig,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
normalizeApitogoConfig,
|
|
5
|
+
type ApitogoJsonConfig,
|
|
6
|
+
} from "./apitogo-config-core.js";
|
|
7
|
+
import type { ApitogoConfig } from "./config.js";
|
|
6
8
|
|
|
7
|
-
type BuildApitogoConfigOptions =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
9
|
+
type BuildApitogoConfigOptions = Partial<ApitogoConfig> &
|
|
10
|
+
ApitogoJsonConfig & {
|
|
11
|
+
plugins?: ApitogoPlugin[];
|
|
12
|
+
};
|
|
11
13
|
|
|
12
14
|
export function buildApitogoConfig({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
plugins,
|
|
16
|
+
plans: _legacyPlans,
|
|
17
|
+
...jsonOverrides
|
|
15
18
|
}: BuildApitogoConfigOptions = {}): ApitogoConfig {
|
|
16
|
-
const jsonConfig =
|
|
19
|
+
const jsonConfig = normalizeApitogoConfig({
|
|
20
|
+
...jsonOverrides,
|
|
21
|
+
...(_legacyPlans !== undefined ? { plans: _legacyPlans } : {}),
|
|
22
|
+
} as ApitogoJsonConfig);
|
|
17
23
|
const siteConfig = extractSiteConfig(jsonConfig);
|
|
24
|
+
const { plans: _rootPlans, ...restOverrides } =
|
|
25
|
+
jsonOverrides as ApitogoJsonConfig;
|
|
18
26
|
|
|
19
27
|
return {
|
|
20
28
|
...siteConfig,
|
|
21
|
-
...
|
|
29
|
+
...restOverrides,
|
|
30
|
+
plugins,
|
|
22
31
|
authentication: {
|
|
23
32
|
...(siteConfig.authentication as ApitogoConfig["authentication"]),
|
|
24
|
-
...
|
|
33
|
+
...jsonOverrides.authentication,
|
|
25
34
|
},
|
|
26
35
|
site: {
|
|
27
36
|
...(siteConfig.site as ApitogoConfig["site"]),
|
|
28
|
-
...
|
|
37
|
+
...jsonOverrides.site,
|
|
29
38
|
},
|
|
30
39
|
metadata: {
|
|
31
40
|
...(siteConfig.metadata as ApitogoConfig["metadata"]),
|
|
32
|
-
...
|
|
41
|
+
...jsonOverrides.metadata,
|
|
33
42
|
},
|
|
34
43
|
docs: {
|
|
35
44
|
...(siteConfig.docs as ApitogoConfig["docs"]),
|
|
36
|
-
...
|
|
45
|
+
...jsonOverrides.docs,
|
|
37
46
|
},
|
|
38
47
|
modules: {
|
|
39
|
-
...(
|
|
40
|
-
...
|
|
48
|
+
...(jsonConfig.modules as ApitogoConfig["modules"]),
|
|
49
|
+
...jsonOverrides.modules,
|
|
41
50
|
},
|
|
42
51
|
} as ApitogoConfig;
|
|
43
52
|
}
|
package/src/config/loader.ts
CHANGED
|
@@ -13,13 +13,21 @@ import { getZudokuRootDir } from "../cli/common/package-json.js";
|
|
|
13
13
|
import { applyAuthenticationHeaderNav } from "../lib/authentication/auth-header-nav.js";
|
|
14
14
|
import { runPluginTransformConfig } from "../lib/core/transform-config.js";
|
|
15
15
|
import invariant from "../lib/util/invariant.js";
|
|
16
|
+
import {
|
|
17
|
+
isBillingEnabled,
|
|
18
|
+
loadApitogoConfig,
|
|
19
|
+
readPlanCatalogItems,
|
|
20
|
+
} from "./apitogo-config-loader.js";
|
|
16
21
|
import { fileExists } from "./file-exists.js";
|
|
17
22
|
import {
|
|
18
23
|
isAuthenticationEnabled,
|
|
19
|
-
isPricingEnabled,
|
|
20
24
|
manifestPathsForEnv,
|
|
25
|
+
manifestToPreviewCatalog,
|
|
26
|
+
plansToPreviewCatalog,
|
|
21
27
|
readEffectiveManifest,
|
|
22
28
|
resolveManifestEnv,
|
|
29
|
+
type DevPortalPreviewPlan,
|
|
30
|
+
type ManifestPlanInput,
|
|
23
31
|
} from "./local-manifest.js";
|
|
24
32
|
import { resolveModulesConfig } from "./resolve-modules.js";
|
|
25
33
|
import type { ResolvedModulesConfig } from "./validators/ModulesSchema.js";
|
|
@@ -35,6 +43,7 @@ export type ConfigWithMeta = ZudokuConfig & {
|
|
|
35
43
|
dependencies: string[];
|
|
36
44
|
pricingEnabled?: boolean;
|
|
37
45
|
authenticationEnabled?: boolean;
|
|
46
|
+
previewPlans?: DevPortalPreviewPlan[];
|
|
38
47
|
};
|
|
39
48
|
__resolvedModules: ResolvedModulesConfig;
|
|
40
49
|
};
|
|
@@ -254,12 +263,20 @@ export async function loadZudokuConfig(
|
|
|
254
263
|
const loadedConfig = await loadZudokuConfigWithMeta(rootDir);
|
|
255
264
|
const manifestEnv = resolveManifestEnv(configEnv.mode);
|
|
256
265
|
const manifest = await readEffectiveManifest(rootDir, manifestEnv);
|
|
266
|
+
const previewCatalog = manifestToPreviewCatalog(manifest);
|
|
267
|
+
const jsonConfig = loadApitogoConfig(rootDir);
|
|
268
|
+
const previewPlans = previewCatalog.plans.length
|
|
269
|
+
? previewCatalog.plans
|
|
270
|
+
: plansToPreviewCatalog(
|
|
271
|
+
readPlanCatalogItems(jsonConfig) as ManifestPlanInput[],
|
|
272
|
+
).plans;
|
|
257
273
|
const configWithManifestMeta: ConfigWithMeta = {
|
|
258
274
|
...loadedConfig,
|
|
259
275
|
__meta: {
|
|
260
276
|
...loadedConfig.__meta,
|
|
261
|
-
pricingEnabled:
|
|
277
|
+
pricingEnabled: isBillingEnabled(jsonConfig),
|
|
262
278
|
authenticationEnabled: isAuthenticationEnabled(manifest),
|
|
279
|
+
previewPlans,
|
|
263
280
|
},
|
|
264
281
|
};
|
|
265
282
|
const transformedConfig = applyAuthenticationHeaderNav(
|
|
@@ -267,6 +284,10 @@ export async function loadZudokuConfig(
|
|
|
267
284
|
);
|
|
268
285
|
config = {
|
|
269
286
|
...transformedConfig,
|
|
287
|
+
__meta: {
|
|
288
|
+
...transformedConfig.__meta,
|
|
289
|
+
...configWithManifestMeta.__meta,
|
|
290
|
+
},
|
|
270
291
|
__resolvedModules: resolveModulesConfig(transformedConfig),
|
|
271
292
|
};
|
|
272
293
|
|
|
@@ -4,16 +4,15 @@ import { z } from "zod";
|
|
|
4
4
|
import {
|
|
5
5
|
APITOGO_CONFIG_FILENAME,
|
|
6
6
|
extractManifest,
|
|
7
|
+
isBillingEnabled,
|
|
7
8
|
loadApitogoConfig,
|
|
9
|
+
readPlanCatalogItems,
|
|
8
10
|
} from "./apitogo-config-loader.js";
|
|
9
11
|
|
|
10
|
-
/** @deprecated
|
|
11
|
-
export const DEV_PORTAL_MANIFEST_FILENAME = "apitogo.local.json";
|
|
12
|
-
|
|
13
|
-
/** @deprecated Use {@link APITOGO_CONFIG_FILENAME}. */
|
|
12
|
+
/** @deprecated Legacy manifest filenames — apitogo.config.json is the only supported source. */
|
|
14
13
|
export const APITOGO_MANIFEST_BASE_FILENAME = "apitogo.json";
|
|
15
14
|
|
|
16
|
-
/** @deprecated
|
|
15
|
+
/** @deprecated Legacy per-environment manifest overrides. */
|
|
17
16
|
export const APITOGO_MANIFEST_ENV_FILES = {
|
|
18
17
|
development: "apitogo.local.json",
|
|
19
18
|
production: "apitogo.prod.json",
|
|
@@ -28,14 +27,18 @@ const BillingPeriodSchema = z.enum(["month", "year", "monthly", "annual"]);
|
|
|
28
27
|
export const ManifestPlanInputSchema = z.object({
|
|
29
28
|
sku: z.string().min(1),
|
|
30
29
|
displayName: z.string().min(1),
|
|
31
|
-
|
|
32
|
-
priceAmount: z.number().optional(),
|
|
30
|
+
priceAmount: z.number().default(0),
|
|
33
31
|
priceCurrency: z.string().optional(),
|
|
34
32
|
billingPeriod: BillingPeriodSchema.optional(),
|
|
35
33
|
limitsJson: z.string().optional(),
|
|
36
34
|
includedActionKeys: z.array(z.string()).optional(),
|
|
37
35
|
});
|
|
38
36
|
|
|
37
|
+
/** Free plans have zero price; paid plans have priceAmount greater than zero. */
|
|
38
|
+
export function isPlanFree(plan: { priceAmount?: number }): boolean {
|
|
39
|
+
return (plan.priceAmount ?? 0) <= 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
39
42
|
export const DevPortalLocalManifestSchema = z.object({
|
|
40
43
|
siteName: z.string().optional(),
|
|
41
44
|
branding: z
|
|
@@ -52,6 +55,8 @@ export const DevPortalLocalManifestSchema = z.object({
|
|
|
52
55
|
authentication: z
|
|
53
56
|
.object({
|
|
54
57
|
enabled: z.boolean().optional(),
|
|
58
|
+
type: z.string().optional(),
|
|
59
|
+
apiBaseUrl: z.string().optional(),
|
|
55
60
|
})
|
|
56
61
|
.optional(),
|
|
57
62
|
plans: z.array(ManifestPlanInputSchema).optional(),
|
|
@@ -76,7 +81,6 @@ export const DevPortalLocalManifestSchema = z.object({
|
|
|
76
81
|
})
|
|
77
82
|
.optional(),
|
|
78
83
|
projectId: z.string().optional(),
|
|
79
|
-
projectName: z.string().optional(),
|
|
80
84
|
});
|
|
81
85
|
|
|
82
86
|
export type ManifestPlanInput = z.infer<typeof ManifestPlanInputSchema>;
|
|
@@ -87,7 +91,6 @@ export type DevPortalLocalManifest = z.infer<
|
|
|
87
91
|
export type DevPortalPreviewPlan = {
|
|
88
92
|
sku: string;
|
|
89
93
|
displayName: string;
|
|
90
|
-
isFree: boolean;
|
|
91
94
|
priceAmount: number;
|
|
92
95
|
priceCurrency: string;
|
|
93
96
|
billingPeriod: string;
|
|
@@ -151,7 +154,6 @@ export function plansToPreviewCatalog(
|
|
|
151
154
|
plans: plans.map((plan) => ({
|
|
152
155
|
sku: plan.sku,
|
|
153
156
|
displayName: plan.displayName,
|
|
154
|
-
isFree: plan.isFree,
|
|
155
157
|
priceAmount: plan.priceAmount ?? 0,
|
|
156
158
|
priceCurrency: plan.priceCurrency ?? "usd",
|
|
157
159
|
billingPeriod: normalizeBillingPeriod(plan.billingPeriod),
|
|
@@ -212,10 +214,7 @@ export function mergeManifest(
|
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
export function manifestFilePath(rootDir: string, filename?: string): string {
|
|
215
|
-
return path.join(
|
|
216
|
-
path.resolve(rootDir),
|
|
217
|
-
filename ?? DEV_PORTAL_MANIFEST_FILENAME,
|
|
218
|
-
);
|
|
217
|
+
return path.join(path.resolve(rootDir), filename ?? APITOGO_CONFIG_FILENAME);
|
|
219
218
|
}
|
|
220
219
|
|
|
221
220
|
export function manifestPathsForEnv(
|
|
@@ -283,60 +282,29 @@ function parseManifestRecord(
|
|
|
283
282
|
|
|
284
283
|
export async function readEffectiveManifest(
|
|
285
284
|
rootDir: string,
|
|
286
|
-
|
|
285
|
+
_env: ApitogoManifestEnv,
|
|
287
286
|
): Promise<DevPortalLocalManifest | null> {
|
|
288
287
|
try {
|
|
289
288
|
const config = loadApitogoConfig(rootDir);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async function readLegacyEffectiveManifest(
|
|
297
|
-
rootDir: string,
|
|
298
|
-
env: ApitogoManifestEnv,
|
|
299
|
-
): Promise<DevPortalLocalManifest | null> {
|
|
300
|
-
const resolvedRoot = path.resolve(rootDir);
|
|
301
|
-
const basePath = path.join(resolvedRoot, APITOGO_MANIFEST_BASE_FILENAME);
|
|
302
|
-
const overridePath = path.join(resolvedRoot, APITOGO_MANIFEST_ENV_FILES[env]);
|
|
303
|
-
const hasBase = await fileExists(basePath);
|
|
304
|
-
const hasOverride = await fileExists(overridePath);
|
|
305
|
-
|
|
306
|
-
if (!hasBase) {
|
|
307
|
-
const legacy = await readManifestFile(
|
|
308
|
-
rootDir,
|
|
309
|
-
APITOGO_MANIFEST_ENV_FILES.development,
|
|
310
|
-
);
|
|
311
|
-
if (legacy) {
|
|
312
|
-
return legacy;
|
|
289
|
+
const manifest = parseManifestRecord(extractManifest(config));
|
|
290
|
+
if (!manifest) {
|
|
291
|
+
return null;
|
|
313
292
|
}
|
|
314
293
|
|
|
315
|
-
if (
|
|
316
|
-
|
|
294
|
+
if (!manifest.plans?.length) {
|
|
295
|
+
const items = readPlanCatalogItems(config);
|
|
296
|
+
if (items.length) {
|
|
297
|
+
manifest.plans = items as ManifestPlanInput[];
|
|
298
|
+
}
|
|
317
299
|
}
|
|
318
300
|
|
|
301
|
+
return {
|
|
302
|
+
...manifest,
|
|
303
|
+
pricing: { ...manifest.pricing, enabled: isBillingEnabled(config) },
|
|
304
|
+
};
|
|
305
|
+
} catch {
|
|
319
306
|
return null;
|
|
320
307
|
}
|
|
321
|
-
|
|
322
|
-
const base = await readManifestFile(rootDir, APITOGO_MANIFEST_BASE_FILENAME);
|
|
323
|
-
if (!base) {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (!hasOverride) {
|
|
328
|
-
return base;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const override = await readManifestFile(
|
|
332
|
-
rootDir,
|
|
333
|
-
APITOGO_MANIFEST_ENV_FILES[env],
|
|
334
|
-
);
|
|
335
|
-
if (!override) {
|
|
336
|
-
return base;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return mergeManifest(base, override);
|
|
340
308
|
}
|
|
341
309
|
|
|
342
310
|
export async function readLocalManifest(
|
|
@@ -141,10 +141,14 @@ export const resolveModulesConfig = (
|
|
|
141
141
|
: !!config.authentication;
|
|
142
142
|
|
|
143
143
|
const apiKeysEnabled = explicit
|
|
144
|
-
? (apiKeysModuleConfig?.enabled ??
|
|
145
|
-
:
|
|
144
|
+
? (apiKeysModuleConfig?.enabled ?? false)
|
|
145
|
+
: false;
|
|
146
146
|
|
|
147
|
-
const
|
|
147
|
+
const billingEnabled =
|
|
148
|
+
(config as { __meta?: { pricingEnabled?: boolean } }).__meta
|
|
149
|
+
?.pricingEnabled === true || plansConfig?.enabled === true;
|
|
150
|
+
|
|
151
|
+
const plansEnabled = billingEnabled;
|
|
148
152
|
|
|
149
153
|
const userPanelEnabled =
|
|
150
154
|
userPanelModule?.enabled ??
|
|
@@ -158,11 +162,10 @@ export const resolveModulesConfig = (
|
|
|
158
162
|
return {
|
|
159
163
|
docs: {
|
|
160
164
|
enabled: docsEnabled,
|
|
161
|
-
files: docsModule?.files
|
|
162
|
-
publishMarkdown:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
llms: docsModule?.llms ?? config.docs?.llms,
|
|
165
|
+
files: docsModule?.files,
|
|
166
|
+
publishMarkdown: docsModule?.publishMarkdown,
|
|
167
|
+
defaultOptions: docsModule?.defaultOptions,
|
|
168
|
+
llms: docsModule?.llms,
|
|
166
169
|
},
|
|
167
170
|
landing: {
|
|
168
171
|
enabled: landingEnabled,
|
|
@@ -203,6 +206,7 @@ export const resolveModulesConfig = (
|
|
|
203
206
|
enabled: plansEnabled,
|
|
204
207
|
path: joinPanelPath(basePath, plansConfig?.path ?? "plans"),
|
|
205
208
|
content: resolvePlansContent(plansConfig?.content),
|
|
209
|
+
items: plansConfig?.items,
|
|
206
210
|
},
|
|
207
211
|
},
|
|
208
212
|
};
|
|
@@ -242,11 +246,10 @@ export const getEffectiveDocsConfig = (
|
|
|
242
246
|
}
|
|
243
247
|
|
|
244
248
|
return {
|
|
245
|
-
files: resolved.docs.files ??
|
|
246
|
-
publishMarkdown:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
llms: resolved.docs.llms ?? config.docs?.llms,
|
|
249
|
+
files: resolved.docs.files ?? [DEFAULT_DOCS_FILES],
|
|
250
|
+
publishMarkdown: resolved.docs.publishMarkdown ?? false,
|
|
251
|
+
defaultOptions: resolved.docs.defaultOptions,
|
|
252
|
+
llms: resolved.docs.llms,
|
|
250
253
|
};
|
|
251
254
|
};
|
|
252
255
|
|
|
@@ -154,6 +154,16 @@ export const ApiKeysModuleSchema = ModuleToggleSchema.extend({
|
|
|
154
154
|
.describe("Route path for API key management."),
|
|
155
155
|
});
|
|
156
156
|
|
|
157
|
+
const PlanCatalogItemSchema = z.object({
|
|
158
|
+
sku: z.string(),
|
|
159
|
+
displayName: z.string(),
|
|
160
|
+
priceAmount: z.number().default(0),
|
|
161
|
+
priceCurrency: z.string().optional(),
|
|
162
|
+
billingPeriod: z.string().optional(),
|
|
163
|
+
limitsJson: z.string().optional(),
|
|
164
|
+
includedActionKeys: z.array(z.string()).optional(),
|
|
165
|
+
});
|
|
166
|
+
|
|
157
167
|
export const PlansModuleSchema = ModuleToggleSchema.extend({
|
|
158
168
|
path: z
|
|
159
169
|
.string()
|
|
@@ -164,6 +174,12 @@ export const PlansModuleSchema = ModuleToggleSchema.extend({
|
|
|
164
174
|
content: PlansContentSchema.describe(
|
|
165
175
|
"Default plans page content when no custom component is provided.",
|
|
166
176
|
),
|
|
177
|
+
items: z
|
|
178
|
+
.array(PlanCatalogItemSchema)
|
|
179
|
+
.optional()
|
|
180
|
+
.describe(
|
|
181
|
+
"Gateway plan catalog for /pricing preview and publish (canonical storage).",
|
|
182
|
+
),
|
|
167
183
|
});
|
|
168
184
|
|
|
169
185
|
export const DashboardModuleSchema = ModuleToggleSchema.extend({
|
|
@@ -288,6 +304,7 @@ export type ResolvedPlansModule = {
|
|
|
288
304
|
enabled: boolean;
|
|
289
305
|
path: string;
|
|
290
306
|
content: ResolvedPlansContent;
|
|
307
|
+
items?: z.infer<typeof PlanCatalogItemSchema>[];
|
|
291
308
|
};
|
|
292
309
|
|
|
293
310
|
export type ResolvedDashboardModule = {
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
type EndUserMeResponse,
|
|
6
6
|
} from "./dev-portal-constants.js";
|
|
7
7
|
|
|
8
|
+
declare const VITE_APITOGO_AUTHENTICATION_API_BASE_URL: string | undefined;
|
|
9
|
+
/** @deprecated Use {@link VITE_APITOGO_AUTHENTICATION_API_BASE_URL}. */
|
|
8
10
|
declare const VITE_APITOGO_DEV_PORTAL_API_URL: string | undefined;
|
|
9
11
|
|
|
10
12
|
export function resolveDevPortalApiBaseUrl(
|
|
@@ -16,9 +18,12 @@ export function resolveDevPortalApiBaseUrl(
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
const fromEnv =
|
|
19
|
-
typeof
|
|
21
|
+
(typeof VITE_APITOGO_AUTHENTICATION_API_BASE_URL === "string"
|
|
22
|
+
? VITE_APITOGO_AUTHENTICATION_API_BASE_URL.trim()
|
|
23
|
+
: "") ||
|
|
24
|
+
(typeof VITE_APITOGO_DEV_PORTAL_API_URL === "string"
|
|
20
25
|
? VITE_APITOGO_DEV_PORTAL_API_URL.trim()
|
|
21
|
-
: "";
|
|
26
|
+
: "");
|
|
22
27
|
|
|
23
28
|
return fromEnv ? fromEnv.replace(/\/+$/, "") : undefined;
|
|
24
29
|
}
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
|
|
2
1
|
import { loadEnv } from "vite";
|
|
3
2
|
|
|
3
|
+
export const AUTHENTICATION_API_BASE_URL_ENV =
|
|
4
|
+
"VITE_APITOGO_AUTHENTICATION_API_BASE_URL";
|
|
5
|
+
|
|
6
|
+
/** @deprecated Use {@link AUTHENTICATION_API_BASE_URL_ENV}. */
|
|
7
|
+
export const LEGACY_DEV_PORTAL_API_URL_ENV = "VITE_APITOGO_DEV_PORTAL_API_URL";
|
|
8
|
+
|
|
4
9
|
export const DEV_PORTAL_VITE_ENV_KEYS = [
|
|
5
|
-
|
|
10
|
+
AUTHENTICATION_API_BASE_URL_ENV,
|
|
11
|
+
LEGACY_DEV_PORTAL_API_URL_ENV,
|
|
6
12
|
] as const;
|
|
7
13
|
|
|
8
14
|
export async function loadDevPortalViteDefines(
|
|
@@ -12,9 +18,11 @@ export async function loadDevPortalViteDefines(
|
|
|
12
18
|
const env = loadEnv(mode, rootDir, "VITE_");
|
|
13
19
|
const defines: Record<string, string> = {};
|
|
14
20
|
|
|
15
|
-
const apiUrl =
|
|
21
|
+
const apiUrl =
|
|
22
|
+
env[AUTHENTICATION_API_BASE_URL_ENV]?.trim() ||
|
|
23
|
+
env[LEGACY_DEV_PORTAL_API_URL_ENV]?.trim();
|
|
16
24
|
if (apiUrl) {
|
|
17
|
-
defines
|
|
25
|
+
defines[AUTHENTICATION_API_BASE_URL_ENV] = JSON.stringify(apiUrl);
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
return defines;
|