robodev 0.19.0 → 0.21.0
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/package.json +1 -1
- package/src/config.test.ts +66 -0
- package/src/config.ts +9 -4
- package/src/emit-starter.test.ts +1 -1
- package/templates/auth-chat/.rulesync/skills/media-feature/SKILL.md +1 -1
- package/templates/auth-chat/README.md +7 -3
- package/templates/auth-chat/apps/fe/openapi-codegen.config.ts +43 -8
- package/templates/auth-chat/apps/fe/vite.config.ts +34 -3
- package/templates/auth-chat/package.json +1 -1
- package/templates/backend/package.json +1 -1
- package/templates/empty/package.json +1 -1
- package/templates/space/.rulesync/skills/media-feature/SKILL.md +1 -1
- package/templates/space/README.md +7 -3
- package/templates/space/apps/fe/openapi-codegen.config.ts +43 -8
- package/templates/space/apps/fe/vite.config.ts +34 -3
- package/templates/space/openapi.json +1 -1
- package/templates/space/package.json +1 -1
- /package/templates/backend/api/files/{presigned-url.ts → upload-request.ts} +0 -0
- /package/templates/space/api/files/{presigned-url.ts → upload-request.ts} +0 -0
package/package.json
CHANGED
package/src/config.test.ts
CHANGED
|
@@ -25,12 +25,78 @@ test("writeViteApiUrl sets Vite and Povio public API URLs", async () => {
|
|
|
25
25
|
);
|
|
26
26
|
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
27
27
|
assert.match(spa, /^\s*configs\s*:/m);
|
|
28
|
+
assert.match(spa, /name:\s*resolved/);
|
|
29
|
+
assert.match(spa, /-\s*name:\s*resolved/);
|
|
28
30
|
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
29
31
|
} finally {
|
|
30
32
|
await rm(root, { recursive: true, force: true });
|
|
31
33
|
}
|
|
32
34
|
});
|
|
33
35
|
|
|
36
|
+
test("writeViteApiUrl upgrades unnamed spa manifest to resolved target", async () => {
|
|
37
|
+
const root = await mkdtemp(join(tmpdir(), "rd-spa-upgrade-"));
|
|
38
|
+
try {
|
|
39
|
+
await mkdir(join(root, ".config"), { recursive: true });
|
|
40
|
+
await writeFile(
|
|
41
|
+
join(root, ".config/local.spa.template.yml"),
|
|
42
|
+
`APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "http://localhost:4000"\nVITE_PUBLIC_API_URL: *APP_PUBLIC_API_URL\n`,
|
|
43
|
+
);
|
|
44
|
+
await writeFile(
|
|
45
|
+
join(root, ".config/local.spa.yml"),
|
|
46
|
+
`configs:
|
|
47
|
+
values:
|
|
48
|
+
- name: "@"
|
|
49
|
+
templateModule: spa.template
|
|
50
|
+
`,
|
|
51
|
+
);
|
|
52
|
+
await writeViteApiUrl("https://prj.example.test", root);
|
|
53
|
+
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
54
|
+
assert.match(spa, /name:\s*resolved/);
|
|
55
|
+
assert.match(spa, /-\s*name:\s*resolved/);
|
|
56
|
+
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
57
|
+
const env = await readFile(join(root, ".env"), "utf8");
|
|
58
|
+
assert.match(env, /^VITE_PUBLIC_API_URL=https:\/\/prj\.example\.test$/m);
|
|
59
|
+
const template = await readFile(join(root, ".config/local.spa.template.yml"), "utf8");
|
|
60
|
+
assert.match(
|
|
61
|
+
template,
|
|
62
|
+
/APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "https:\/\/prj\.example\.test"/,
|
|
63
|
+
);
|
|
64
|
+
} finally {
|
|
65
|
+
await rm(root, { recursive: true, force: true });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("writeViteApiUrl leaves a resolved spa manifest in place", async () => {
|
|
70
|
+
const root = await mkdtemp(join(tmpdir(), "rd-spa-ok-"));
|
|
71
|
+
try {
|
|
72
|
+
await mkdir(join(root, ".config"), { recursive: true });
|
|
73
|
+
await writeFile(
|
|
74
|
+
join(root, ".config/local.spa.template.yml"),
|
|
75
|
+
`APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "http://localhost:4000"\nVITE_PUBLIC_API_URL: *APP_PUBLIC_API_URL\n`,
|
|
76
|
+
);
|
|
77
|
+
const good = `configs:
|
|
78
|
+
- name: resolved
|
|
79
|
+
values:
|
|
80
|
+
- name: "@"
|
|
81
|
+
templateModule: spa.template
|
|
82
|
+
`;
|
|
83
|
+
await writeFile(join(root, ".config/local.spa.yml"), good);
|
|
84
|
+
await writeViteApiUrl("https://prj.example.test", root);
|
|
85
|
+
const spa = await readFile(join(root, ".config/local.spa.yml"), "utf8");
|
|
86
|
+
assert.match(spa, /name:\s*resolved/);
|
|
87
|
+
assert.match(spa, /templateModule:\s*spa\.template/);
|
|
88
|
+
const env = await readFile(join(root, ".env"), "utf8");
|
|
89
|
+
assert.match(env, /^VITE_PUBLIC_API_URL=https:\/\/prj\.example\.test$/m);
|
|
90
|
+
const template = await readFile(join(root, ".config/local.spa.template.yml"), "utf8");
|
|
91
|
+
assert.match(
|
|
92
|
+
template,
|
|
93
|
+
/APP_PUBLIC_API_URL: &APP_PUBLIC_API_URL "https:\/\/prj\.example\.test"/,
|
|
94
|
+
);
|
|
95
|
+
} finally {
|
|
96
|
+
await rm(root, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
34
100
|
test("writeLink merges frontend and unknown keys", async () => {
|
|
35
101
|
const root = await mkdtemp(join(tmpdir(), "rd-link-"));
|
|
36
102
|
try {
|
package/src/config.ts
CHANGED
|
@@ -23,9 +23,10 @@ export type ProjectLink = {
|
|
|
23
23
|
const PROD_URL = "https://robodev.povio.dev";
|
|
24
24
|
|
|
25
25
|
const SPA_MANIFEST = `configs:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
- name: resolved
|
|
27
|
+
values:
|
|
28
|
+
- name: "@"
|
|
29
|
+
templateModule: spa.template
|
|
29
30
|
`;
|
|
30
31
|
|
|
31
32
|
/** Control-plane API. Override with `STARBASE_URL` for local Starbase. */
|
|
@@ -157,6 +158,10 @@ function isSpaManifest(content: string): boolean {
|
|
|
157
158
|
return /^\s*configs\s*:/m.test(content);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
function hasResolvedSpaTarget(content: string): boolean {
|
|
162
|
+
return /name:\s*resolved/.test(content);
|
|
163
|
+
}
|
|
164
|
+
|
|
160
165
|
function rewriteApiUrlInSpaValues(source: string, projectApiUrl: string): string {
|
|
161
166
|
const quoted = JSON.stringify(projectApiUrl);
|
|
162
167
|
const next = source.replace(/^(APP_PUBLIC_API_URL:\s*&APP_PUBLIC_API_URL\s+).+$/m, `$1${quoted}`);
|
|
@@ -209,7 +214,7 @@ async function rewriteLocalSpaConfig(projectApiUrl: string, cwd: string): Promis
|
|
|
209
214
|
await writeFile(templatePath, next.endsWith("\n") ? next : `${next}\n`);
|
|
210
215
|
}
|
|
211
216
|
|
|
212
|
-
if (!dest || !isSpaManifest(dest)) {
|
|
217
|
+
if (!dest || !isSpaManifest(dest) || !hasResolvedSpaTarget(dest)) {
|
|
213
218
|
await writeFile(destPath, SPA_MANIFEST);
|
|
214
219
|
}
|
|
215
220
|
}
|
package/src/emit-starter.test.ts
CHANGED
|
@@ -84,7 +84,7 @@ test("collectFiles for space starter is Tiny APIs without apps/fe", async () =>
|
|
|
84
84
|
assert.ok(paths.has("api/planets.ts"));
|
|
85
85
|
assert.ok(paths.has("api/planets/paginate.ts"));
|
|
86
86
|
assert.ok(paths.has("api/aliens/labels.ts"));
|
|
87
|
-
assert.ok(paths.has("api/files/
|
|
87
|
+
assert.ok(paths.has("api/files/upload-request.ts"));
|
|
88
88
|
assert.ok(paths.has("api/files/upload.ts"));
|
|
89
89
|
assert.ok(!paths.has("index.html"));
|
|
90
90
|
assert.ok(!paths.has("src/main.tsx"));
|
|
@@ -11,7 +11,7 @@ codexcli:
|
|
|
11
11
|
Preserve the upload contract:
|
|
12
12
|
|
|
13
13
|
1. Frontend selects a `File`.
|
|
14
|
-
2. Frontend calls `MediaQueries.useUploadRequest` with metadata only (`POST /api/files/
|
|
14
|
+
2. Frontend calls `MediaQueries.useUploadRequest` with metadata only (`POST /api/files/upload-request`).
|
|
15
15
|
3. Frontend uploads bytes to the returned URL (`POST /api/files/upload` via Starbase `ctx.storage`).
|
|
16
16
|
4. Feature create/update submits only `image: { id }`.
|
|
17
17
|
5. Feature reads resolve that ID to `image: { id, url }`.
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Starter from `robodev create --starter auth-chat`: Tiny chat APIs (`chat` database, `messages` table), authenticated invite email, and a Povio UI in `apps/fe`.
|
|
4
4
|
|
|
5
|
-
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
5
|
+
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
6
|
+
|
|
7
|
+
Local Starbase occupies FE **:3000** and BE **:4000**. Point the CLI at it, then run starter Vite on **3001** so it does not steal the dashboard port:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
10
|
+
STARBASE_URL=http://localhost:4000 STARBASE_FE_URL=http://localhost:3000
|
|
11
|
+
# from starter root: auth / link / deploy as today
|
|
8
12
|
cd apps/fe
|
|
9
13
|
bun install
|
|
10
14
|
bun openapi:gen
|
|
11
|
-
bun
|
|
15
|
+
bunx --bun vite --port 3001
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
`bun` is required for `apps/fe`.
|
|
15
19
|
|
|
16
20
|
Auth, link, and deploy from this **root**:
|
|
17
21
|
|
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
/* oxlint-disable import/no-nodejs-modules */
|
|
2
2
|
import type { OpenAPICodegenConfig } from "@povio/openapi-codegen-cli";
|
|
3
3
|
import { resolveConfigSync } from "@povio/resolve-config";
|
|
4
|
-
import {
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
5
6
|
|
|
6
7
|
const COMPACT_OPENAPI_INPUT = "../../openapi.json";
|
|
7
8
|
const repoRoot = resolve(process.cwd(), "../..");
|
|
8
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as Record<
|
|
9
|
-
string,
|
|
10
|
-
string | undefined
|
|
11
|
-
>;
|
|
12
9
|
|
|
13
|
-
function
|
|
14
|
-
|
|
10
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
11
|
+
try {
|
|
12
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
13
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
14
|
+
const value = line?.slice(key.length + 1).trim();
|
|
15
|
+
return value || undefined;
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, string | undefined> {
|
|
22
|
+
try {
|
|
23
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
24
|
+
if (resolved && typeof resolved === "object") {
|
|
25
|
+
return resolved as Record<string, string | undefined>;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// unnamed or missing spa manifest
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
34
|
+
|
|
35
|
+
function fallbackApiUrl(): string {
|
|
36
|
+
return (
|
|
37
|
+
env.VITE_PUBLIC_API_URL ||
|
|
38
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
39
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
40
|
+
"http://localhost:4000"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveLiveOpenApiInput(apiUrl = fallbackApiUrl()) {
|
|
45
|
+
const usable = apiUrl.trim();
|
|
46
|
+
if (!usable) return COMPACT_OPENAPI_INPUT;
|
|
47
|
+
return `${usable.replace(/\/+$/, "")}/openapi.json`;
|
|
15
48
|
}
|
|
16
49
|
|
|
17
50
|
export function resolveOpenApiInput() {
|
|
18
|
-
|
|
51
|
+
if (process.env.OPENAPI_INPUT) return process.env.OPENAPI_INPUT;
|
|
52
|
+
if (process.env.OPENAPI_LIVE === "true") return resolveLiveOpenApiInput();
|
|
53
|
+
return COMPACT_OPENAPI_INPUT;
|
|
19
54
|
}
|
|
20
55
|
|
|
21
56
|
const config: OpenAPICodegenConfig = {
|
|
@@ -4,16 +4,47 @@ import tailwindCSS from "@tailwindcss/vite";
|
|
|
4
4
|
import { devtools } from "@tanstack/devtools-vite";
|
|
5
5
|
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
|
6
6
|
import viteReact from "@vitejs/plugin-react";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
7
9
|
import { defineConfig as defineViteConfig, mergeConfig } from "vite";
|
|
8
10
|
import devtoolsJson from "vite-plugin-devtools-json";
|
|
9
11
|
import { defineConfig as defineVitestConfig } from "vitest/config";
|
|
10
12
|
import { reactIconsSprite } from "react-icons-sprite/vite";
|
|
11
13
|
|
|
12
14
|
const repoRoot = new URL("../..", import.meta.url).pathname;
|
|
13
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as any;
|
|
14
15
|
|
|
16
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
19
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
20
|
+
const value = line?.slice(key.length + 1).trim();
|
|
21
|
+
return value || undefined;
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, unknown> {
|
|
28
|
+
try {
|
|
29
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
30
|
+
if (resolved && typeof resolved === "object") {
|
|
31
|
+
return resolved as Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// unnamed or missing spa manifest
|
|
35
|
+
}
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
15
40
|
applyEnv(env, "__");
|
|
16
41
|
|
|
42
|
+
const apiUrl =
|
|
43
|
+
getString(env, "VITE_PUBLIC_API_URL") ||
|
|
44
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
45
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
46
|
+
"http://localhost:4000";
|
|
47
|
+
|
|
17
48
|
const isInTestMode = process.env.VITEST === "true";
|
|
18
49
|
const iconsSpritePlugin = reactIconsSprite();
|
|
19
50
|
const iconsSpriteTransform = iconsSpritePlugin.transform;
|
|
@@ -51,11 +82,11 @@ const createViteConfig = (isDevServer: boolean) => ({
|
|
|
51
82
|
],
|
|
52
83
|
server: {
|
|
53
84
|
open: false,
|
|
54
|
-
port: getNumber(env, "VITE_DEV_PORT"),
|
|
85
|
+
port: getNumber(env, "VITE_DEV_PORT") ?? 3000,
|
|
55
86
|
strictPort: false,
|
|
56
87
|
proxy: {
|
|
57
88
|
"^/api(?:/|$)": {
|
|
58
|
-
target:
|
|
89
|
+
target: apiUrl,
|
|
59
90
|
changeOrigin: true,
|
|
60
91
|
secure: false,
|
|
61
92
|
},
|
|
@@ -11,7 +11,7 @@ codexcli:
|
|
|
11
11
|
Preserve the upload contract:
|
|
12
12
|
|
|
13
13
|
1. Frontend selects a `File`.
|
|
14
|
-
2. Frontend calls `MediaQueries.useUploadRequest` with metadata only (`POST /api/files/
|
|
14
|
+
2. Frontend calls `MediaQueries.useUploadRequest` with metadata only (`POST /api/files/upload-request`).
|
|
15
15
|
3. Frontend uploads bytes to the returned URL (`POST /api/files/upload` via Starbase `ctx.storage`).
|
|
16
16
|
4. Feature create/update submits only `image: { id }`.
|
|
17
17
|
5. Feature reads resolve that ID to `image: { id, url }`.
|
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Starter from `robodev create`: Tiny Template schema (`planets`, `aliens`, `media`, `planet_likes`), file-based APIs in `api/`, and a Povio UI in `apps/fe`.
|
|
4
4
|
|
|
5
|
-
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
5
|
+
After a CLI create or deploy with `.robodev` `frontend` set (the default for this starter), the project host serves the built Tiny SPA at `GET /`. APIs and Swagger still win over static files.
|
|
6
|
+
|
|
7
|
+
Local Starbase occupies FE **:3000** and BE **:4000**. Point the CLI at it, then run starter Vite on **3001** so it does not steal the dashboard port:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
10
|
+
STARBASE_URL=http://localhost:4000 STARBASE_FE_URL=http://localhost:3000
|
|
11
|
+
# from starter root: auth / link / deploy as today
|
|
8
12
|
cd apps/fe
|
|
9
13
|
bun install
|
|
10
14
|
bun openapi:gen
|
|
11
|
-
bun
|
|
15
|
+
bunx --bun vite --port 3001
|
|
12
16
|
```
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
`bun` is required for `apps/fe`.
|
|
15
19
|
|
|
16
20
|
Auth, link, and deploy from this **root**:
|
|
17
21
|
|
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
/* oxlint-disable import/no-nodejs-modules */
|
|
2
2
|
import type { OpenAPICodegenConfig } from "@povio/openapi-codegen-cli";
|
|
3
3
|
import { resolveConfigSync } from "@povio/resolve-config";
|
|
4
|
-
import {
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
5
6
|
|
|
6
7
|
const COMPACT_OPENAPI_INPUT = "../../openapi.json";
|
|
7
8
|
const repoRoot = resolve(process.cwd(), "../..");
|
|
8
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as Record<
|
|
9
|
-
string,
|
|
10
|
-
string | undefined
|
|
11
|
-
>;
|
|
12
9
|
|
|
13
|
-
function
|
|
14
|
-
|
|
10
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
11
|
+
try {
|
|
12
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
13
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
14
|
+
const value = line?.slice(key.length + 1).trim();
|
|
15
|
+
return value || undefined;
|
|
16
|
+
} catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, string | undefined> {
|
|
22
|
+
try {
|
|
23
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
24
|
+
if (resolved && typeof resolved === "object") {
|
|
25
|
+
return resolved as Record<string, string | undefined>;
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
// unnamed or missing spa manifest
|
|
29
|
+
}
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
34
|
+
|
|
35
|
+
function fallbackApiUrl(): string {
|
|
36
|
+
return (
|
|
37
|
+
env.VITE_PUBLIC_API_URL ||
|
|
38
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
39
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
40
|
+
"http://localhost:4000"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function resolveLiveOpenApiInput(apiUrl = fallbackApiUrl()) {
|
|
45
|
+
const usable = apiUrl.trim();
|
|
46
|
+
if (!usable) return COMPACT_OPENAPI_INPUT;
|
|
47
|
+
return `${usable.replace(/\/+$/, "")}/openapi.json`;
|
|
15
48
|
}
|
|
16
49
|
|
|
17
50
|
export function resolveOpenApiInput() {
|
|
18
|
-
|
|
51
|
+
if (process.env.OPENAPI_INPUT) return process.env.OPENAPI_INPUT;
|
|
52
|
+
if (process.env.OPENAPI_LIVE === "true") return resolveLiveOpenApiInput();
|
|
53
|
+
return COMPACT_OPENAPI_INPUT;
|
|
19
54
|
}
|
|
20
55
|
|
|
21
56
|
const config: OpenAPICodegenConfig = {
|
|
@@ -4,16 +4,47 @@ import tailwindCSS from "@tailwindcss/vite";
|
|
|
4
4
|
import { devtools } from "@tanstack/devtools-vite";
|
|
5
5
|
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
|
6
6
|
import viteReact from "@vitejs/plugin-react";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
7
9
|
import { defineConfig as defineViteConfig, mergeConfig } from "vite";
|
|
8
10
|
import devtoolsJson from "vite-plugin-devtools-json";
|
|
9
11
|
import { defineConfig as defineVitestConfig } from "vitest/config";
|
|
10
12
|
import { reactIconsSprite } from "react-icons-sprite/vite";
|
|
11
13
|
|
|
12
14
|
const repoRoot = new URL("../..", import.meta.url).pathname;
|
|
13
|
-
const env = resolveConfigSync({ cwd: repoRoot, module: "spa", target: "resolved" }) as any;
|
|
14
15
|
|
|
16
|
+
function readDotEnvKey(root: string, key: string): string | undefined {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(join(root, ".env"), "utf8");
|
|
19
|
+
const line = content.split(/\r?\n/).find((row) => row.startsWith(`${key}=`));
|
|
20
|
+
const value = line?.slice(key.length + 1).trim();
|
|
21
|
+
return value || undefined;
|
|
22
|
+
} catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadResolvedSpaEnv(cwd: string): Record<string, unknown> {
|
|
28
|
+
try {
|
|
29
|
+
const resolved = resolveConfigSync({ cwd, module: "spa", target: "resolved" });
|
|
30
|
+
if (resolved && typeof resolved === "object") {
|
|
31
|
+
return resolved as Record<string, unknown>;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
// unnamed or missing spa manifest
|
|
35
|
+
}
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const env = loadResolvedSpaEnv(repoRoot);
|
|
15
40
|
applyEnv(env, "__");
|
|
16
41
|
|
|
42
|
+
const apiUrl =
|
|
43
|
+
getString(env, "VITE_PUBLIC_API_URL") ||
|
|
44
|
+
process.env.VITE_PUBLIC_API_URL ||
|
|
45
|
+
readDotEnvKey(repoRoot, "VITE_PUBLIC_API_URL") ||
|
|
46
|
+
"http://localhost:4000";
|
|
47
|
+
|
|
17
48
|
const isInTestMode = process.env.VITEST === "true";
|
|
18
49
|
const iconsSpritePlugin = reactIconsSprite();
|
|
19
50
|
const iconsSpriteTransform = iconsSpritePlugin.transform;
|
|
@@ -51,11 +82,11 @@ const createViteConfig = (isDevServer: boolean) => ({
|
|
|
51
82
|
],
|
|
52
83
|
server: {
|
|
53
84
|
open: false,
|
|
54
|
-
port: getNumber(env, "VITE_DEV_PORT"),
|
|
85
|
+
port: getNumber(env, "VITE_DEV_PORT") ?? 3000,
|
|
55
86
|
strictPort: false,
|
|
56
87
|
proxy: {
|
|
57
88
|
"^/api(?:/|$)": {
|
|
58
|
-
target:
|
|
89
|
+
target: apiUrl,
|
|
59
90
|
changeOrigin: true,
|
|
60
91
|
secure: false,
|
|
61
92
|
},
|
|
@@ -1209,7 +1209,7 @@
|
|
|
1209
1209
|
"x-bl": "Consumes a valid unused password reset code."
|
|
1210
1210
|
}
|
|
1211
1211
|
},
|
|
1212
|
-
"/api/files/
|
|
1212
|
+
"/api/files/upload-request": {
|
|
1213
1213
|
"post": {
|
|
1214
1214
|
"operationId": "MediaLibraryController_uploadRequest",
|
|
1215
1215
|
"tags": ["Media"],
|
|
File without changes
|
|
File without changes
|