@jskit-ai/create-app 0.1.94 → 0.1.95
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 +2 -2
- package/src/server/index.js +8 -1
- package/templates/base-shell/.jskit/lock.json +229 -0
- package/templates/base-shell/package.json +4 -2
- package/templates/base-shell/packages/main/src/client/providers/MainClientProvider.js +8 -0
- package/templates/base-shell/src/App.vue +7 -9
- package/templates/base-shell/src/components/ShellLayout.vue +12 -0
- package/templates/base-shell/src/components/menus/MenuLinkItem.vue +30 -0
- package/templates/base-shell/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/templates/base-shell/src/components/menus/TabLinkItem.vue +42 -0
- package/templates/base-shell/src/error.js +19 -0
- package/templates/base-shell/src/pages/home/index.vue +85 -21
- package/templates/base-shell/src/pages/home/settings/general/index.vue +40 -0
- package/templates/base-shell/src/pages/home/settings/index.vue +7 -0
- package/templates/base-shell/src/pages/home/settings.vue +109 -0
- package/templates/base-shell/src/pages/home.vue +8 -1
- package/templates/base-shell/src/placement.js +56 -0
- package/templates/base-shell/src/placementTopology.js +149 -0
- package/templates/base-shell/tests/e2e/adaptive-shell.spec.ts +4 -0
- package/templates/base-shell/tests/e2e/base-shell.spec.ts +1 -1
- package/templates/minimal-shell/.github/workflows/verify.yml +29 -0
- package/templates/minimal-shell/.jskit/lock.json +31 -0
- package/templates/minimal-shell/AGENTS.md +10 -0
- package/templates/minimal-shell/Procfile +2 -0
- package/templates/minimal-shell/app.json +14 -0
- package/templates/minimal-shell/bin/server.js +8 -0
- package/templates/minimal-shell/config/public.js +41 -0
- package/templates/minimal-shell/config/server.js +1 -0
- package/templates/minimal-shell/config/surfaceAccessPolicies.js +3 -0
- package/templates/minimal-shell/eslint.config.mjs +19 -0
- package/templates/minimal-shell/favicon.svg +7 -0
- package/templates/minimal-shell/gitignore +8 -0
- package/templates/minimal-shell/index.html +13 -0
- package/templates/minimal-shell/jsconfig.json +8 -0
- package/templates/minimal-shell/package.json +55 -0
- package/templates/minimal-shell/packages/main/package.descriptor.mjs +69 -0
- package/templates/minimal-shell/packages/main/package.json +12 -0
- package/templates/minimal-shell/packages/main/src/client/index.js +13 -0
- package/templates/minimal-shell/packages/main/src/client/providers/MainClientProvider.js +20 -0
- package/templates/minimal-shell/packages/main/src/server/MainServiceProvider.js +20 -0
- package/templates/minimal-shell/packages/main/src/server/index.js +1 -0
- package/templates/minimal-shell/packages/main/src/server/loadAppConfig.js +9 -0
- package/templates/minimal-shell/packages/main/src/shared/index.js +10 -0
- package/templates/minimal-shell/packages/main/src/shared/schemas/index.js +22 -0
- package/templates/minimal-shell/scripts/dokku-set-remote-envs-via-ssh.sh +210 -0
- package/templates/minimal-shell/server/lib/runtimeEnv.js +45 -0
- package/templates/minimal-shell/server/lib/surfaceRuntime.js +10 -0
- package/templates/minimal-shell/server.js +189 -0
- package/templates/minimal-shell/src/App.vue +13 -0
- package/templates/minimal-shell/src/main.js +85 -0
- package/templates/minimal-shell/src/pages/home/index.vue +48 -0
- package/templates/minimal-shell/src/pages/home.vue +13 -0
- package/templates/minimal-shell/src/views/NotFound.vue +13 -0
- package/templates/minimal-shell/tests/client/smoke.vitest.js +7 -0
- package/templates/minimal-shell/tests/e2e/base-shell.spec.ts +57 -0
- package/templates/minimal-shell/tests/server/minimalShell.validator.test.js +131 -0
- package/templates/minimal-shell/tests/server/smoke.test.js +16 -0
- package/templates/minimal-shell/vite.config.mjs +84 -0
- package/templates/minimal-shell/vite.shared.mjs +59 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section class="generated-ui-screen generated-ui-screen--app home-start-screen d-flex flex-column ga-4">
|
|
3
|
+
<header>
|
|
4
|
+
<p class="text-overline text-medium-emphasis mb-1">Home</p>
|
|
5
|
+
<h1 class="home-start-screen__title">Home base</h1>
|
|
6
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
7
|
+
The app runtime is online and ready for the first real workflow.
|
|
8
|
+
</p>
|
|
9
|
+
</header>
|
|
10
|
+
|
|
11
|
+
<v-sheet rounded="lg" border class="home-start-screen__panel">
|
|
12
|
+
<h2 class="text-h6 mb-2">No activity yet</h2>
|
|
13
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
14
|
+
Recent work, saved records, and next actions will appear here once this app has data.
|
|
15
|
+
</p>
|
|
16
|
+
</v-sheet>
|
|
17
|
+
</section>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<style scoped>
|
|
21
|
+
.generated-ui-screen {
|
|
22
|
+
--generated-ui-screen-title-size: 2rem;
|
|
23
|
+
--generated-ui-screen-panel-padding: 1rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.home-start-screen {
|
|
27
|
+
margin-inline: auto;
|
|
28
|
+
max-width: 48rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.home-start-screen__title {
|
|
32
|
+
font-size: var(--generated-ui-screen-title-size);
|
|
33
|
+
font-weight: 700;
|
|
34
|
+
letter-spacing: 0;
|
|
35
|
+
line-height: 1.08;
|
|
36
|
+
margin: 0 0 0.45rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.home-start-screen__panel {
|
|
40
|
+
padding: var(--generated-ui-screen-panel-padding);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (max-width: 640px) {
|
|
44
|
+
.generated-ui-screen {
|
|
45
|
+
--generated-ui-screen-title-size: 1.5rem;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const title = "Not Found";
|
|
3
|
+
const message = "The page you requested does not exist.";
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
7
|
+
<v-container class="fill-height d-flex align-center justify-center">
|
|
8
|
+
<v-card class="pa-6 text-center" max-width="560" rounded="lg">
|
|
9
|
+
<v-card-title class="text-h4">{{ title }}</v-card-title>
|
|
10
|
+
<v-card-text class="text-medium-emphasis">{{ message }}</v-card-text>
|
|
11
|
+
</v-card>
|
|
12
|
+
</v-container>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
const BASE_URL = String(process.env.PLAYWRIGHT_BASE_URL || "http://127.0.0.1:5173").replace(/\/+$/u, "");
|
|
4
|
+
const SMOKE_PATH = String(process.env.JSKIT_PLAYWRIGHT_SMOKE_PATH || "/home");
|
|
5
|
+
|
|
6
|
+
const viewports = [
|
|
7
|
+
{ name: "compact", width: 390, height: 844 },
|
|
8
|
+
{ name: "medium", width: 768, height: 1024 },
|
|
9
|
+
{ name: "expanded", width: 1280, height: 900 }
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
async function expectNoHorizontalOverflow(page) {
|
|
13
|
+
const metrics = await page.evaluate(() => ({
|
|
14
|
+
clientWidth: document.documentElement.clientWidth,
|
|
15
|
+
scrollWidth: document.documentElement.scrollWidth
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
expect(metrics.scrollWidth).toBeLessThanOrEqual(metrics.clientWidth + 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function expectGeneratedScreenContract(page) {
|
|
22
|
+
const screen = page.locator(".generated-ui-screen").first();
|
|
23
|
+
|
|
24
|
+
await expect(screen).toBeVisible();
|
|
25
|
+
await expect(screen).toHaveClass(/generated-ui-screen--app/u);
|
|
26
|
+
await expect(screen.locator("h1").first()).toBeVisible();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function expectVisibleTapTargets(page) {
|
|
30
|
+
const targetHeights = await page.locator("a[href], button, [role='button'], .v-btn, .v-list-item").evaluateAll(
|
|
31
|
+
(elements) => elements
|
|
32
|
+
.filter((element) => {
|
|
33
|
+
const rect = element.getBoundingClientRect();
|
|
34
|
+
const style = window.getComputedStyle(element);
|
|
35
|
+
return style.display !== "none" && style.visibility !== "hidden" && rect.width > 0 && rect.height > 0;
|
|
36
|
+
})
|
|
37
|
+
.map((element) => element.getBoundingClientRect().height)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
for (const height of targetHeights) {
|
|
41
|
+
expect(height).toBeGreaterThanOrEqual(48);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
test.describe("generated base app responsive smoke", () => {
|
|
46
|
+
for (const viewport of viewports) {
|
|
47
|
+
test(`${viewport.name} home route renders without horizontal overflow`, async ({ page }) => {
|
|
48
|
+
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
|
49
|
+
await page.goto(`${BASE_URL}${SMOKE_PATH}`);
|
|
50
|
+
await expect(page.locator("body")).toBeVisible();
|
|
51
|
+
await expect(page.getByText("Home base")).toBeVisible();
|
|
52
|
+
await expectGeneratedScreenContract(page);
|
|
53
|
+
await expectVisibleTapTargets(page);
|
|
54
|
+
await expectNoHorizontalOverflow(page);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { access, readdir, readFile } from "node:fs/promises";
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const APP_ROOT = path.resolve(__dirname, "../..");
|
|
10
|
+
|
|
11
|
+
const EXPECTED_MANAGED_SCRIPTS = Object.freeze({
|
|
12
|
+
devlinks: "jskit app link-local-packages",
|
|
13
|
+
verify: "jskit app verify && npm run --if-present verify:app",
|
|
14
|
+
release: "jskit app release",
|
|
15
|
+
"jskit:update": "jskit app update-packages"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const REQUIRED_TOP_LEVEL_ENTRIES = Object.freeze([
|
|
19
|
+
"AGENTS.md",
|
|
20
|
+
"app.json",
|
|
21
|
+
"Procfile",
|
|
22
|
+
"bin",
|
|
23
|
+
"config",
|
|
24
|
+
"eslint.config.mjs",
|
|
25
|
+
"favicon.svg",
|
|
26
|
+
"index.html",
|
|
27
|
+
"jsconfig.json",
|
|
28
|
+
"package.json",
|
|
29
|
+
"packages",
|
|
30
|
+
"scripts",
|
|
31
|
+
"server",
|
|
32
|
+
"server.js",
|
|
33
|
+
"src",
|
|
34
|
+
"tests",
|
|
35
|
+
"vite.config.mjs",
|
|
36
|
+
"vite.shared.mjs"
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
async function readPackageJson() {
|
|
40
|
+
const packageJsonPath = path.join(APP_ROOT, "package.json");
|
|
41
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
42
|
+
return JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function readTopLevelEntries() {
|
|
46
|
+
const entries = await readdir(APP_ROOT, { withFileTypes: true });
|
|
47
|
+
const ignored = new Set([
|
|
48
|
+
"node_modules",
|
|
49
|
+
"dist",
|
|
50
|
+
"coverage",
|
|
51
|
+
"test-results",
|
|
52
|
+
"package-lock.json",
|
|
53
|
+
"pnpm-lock.yaml",
|
|
54
|
+
"yarn.lock"
|
|
55
|
+
]);
|
|
56
|
+
return entries
|
|
57
|
+
.map((entry) => entry.name)
|
|
58
|
+
.filter((name) => !name.startsWith("."))
|
|
59
|
+
.filter((name) => !ignored.has(name));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function listFilesRecursive(directory) {
|
|
63
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
64
|
+
const files = [];
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const absolutePath = path.join(directory, entry.name);
|
|
67
|
+
if (entry.isDirectory()) {
|
|
68
|
+
files.push(...(await listFilesRecursive(absolutePath)));
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
files.push(absolutePath);
|
|
72
|
+
}
|
|
73
|
+
return files;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
test("latest JSKIT app keeps managed wrapper scripts and JSKIT dependency specifiers", async () => {
|
|
77
|
+
const packageJson = await readPackageJson();
|
|
78
|
+
const runtimeDependencies = Object.entries(packageJson.dependencies || {}).filter(([name]) =>
|
|
79
|
+
name.startsWith("@jskit-ai/")
|
|
80
|
+
);
|
|
81
|
+
const devDependencies = Object.entries(packageJson.devDependencies || {}).filter(([name]) =>
|
|
82
|
+
name.startsWith("@jskit-ai/")
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
assert.ok(runtimeDependencies.length > 0, "Expected JSKIT runtime dependencies to be present.");
|
|
86
|
+
assert.ok(devDependencies.length > 0, "Expected JSKIT dev dependencies to be present.");
|
|
87
|
+
|
|
88
|
+
for (const [name, value] of [...runtimeDependencies, ...devDependencies]) {
|
|
89
|
+
assert.match(value, /^(0\.x|\d+\.\d+\.\d+)$/, `Expected ${name} to use a managed JSKIT version specifier.`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const [scriptName, expectedValue] of Object.entries(EXPECTED_MANAGED_SCRIPTS)) {
|
|
93
|
+
assert.equal(packageJson.scripts?.[scriptName], expectedValue, `Unexpected ${scriptName} script.`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("latest JSKIT scaffold files are present at the app root", async () => {
|
|
98
|
+
const entries = await readTopLevelEntries();
|
|
99
|
+
for (const requiredEntry of REQUIRED_TOP_LEVEL_ENTRIES) {
|
|
100
|
+
assert.equal(entries.includes(requiredEntry), true, `Missing top-level scaffold entry: ${requiredEntry}`);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("starter shell keeps the default hosted CI workflow simple", async () => {
|
|
105
|
+
const workflowSource = await readFile(path.join(APP_ROOT, ".github", "workflows", "verify.yml"), "utf8");
|
|
106
|
+
|
|
107
|
+
assert.match(workflowSource, /run: npm run verify/);
|
|
108
|
+
assert.doesNotMatch(workflowSource, /jskit app verify --against/);
|
|
109
|
+
assert.doesNotMatch(workflowSource, /jskit app verify-ui/);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("starter shell does not include the app.manifest scaffold", async () => {
|
|
113
|
+
await assert.rejects(access(path.join(APP_ROOT, "framework/app.manifest.mjs")), /ENOENT/);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("local package source avoids brittle deep relative imports", async () => {
|
|
117
|
+
const packageSourceFiles = await listFilesRecursive(path.join(APP_ROOT, "packages"));
|
|
118
|
+
const brittleImportPattern = /\bfrom\s+["'](?:\.\.\/){5,}[^"']+["']/;
|
|
119
|
+
|
|
120
|
+
for (const filePath of packageSourceFiles) {
|
|
121
|
+
if (!filePath.endsWith(".js") && !filePath.endsWith(".mjs")) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const source = await readFile(filePath, "utf8");
|
|
125
|
+
assert.equal(
|
|
126
|
+
brittleImportPattern.test(source),
|
|
127
|
+
false,
|
|
128
|
+
`Found brittle deep relative import in ${path.relative(APP_ROOT, filePath)}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createServer } from "../../server.js";
|
|
4
|
+
|
|
5
|
+
test("GET /api/health returns built-in health response", async () => {
|
|
6
|
+
const app = await createServer();
|
|
7
|
+
const response = await app.inject({
|
|
8
|
+
method: "GET",
|
|
9
|
+
url: "/api/health"
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
assert.equal(response.statusCode, 200);
|
|
13
|
+
assert.equal(response.json().ok, true);
|
|
14
|
+
|
|
15
|
+
await app.close();
|
|
16
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { fileURLToPath, URL } from "node:url";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import vue from "@vitejs/plugin-vue";
|
|
4
|
+
import vuetify from "vite-plugin-vuetify";
|
|
5
|
+
import VueRouter from "vue-router/vite";
|
|
6
|
+
import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
|
|
7
|
+
import { loadViteDevProxyEntries, toPositiveInt } from "./vite.shared.mjs";
|
|
8
|
+
|
|
9
|
+
const devPort = toPositiveInt(process.env.VITE_DEV_PORT, 5173);
|
|
10
|
+
const apiProxyTarget = String(process.env.VITE_API_PROXY_TARGET || "").trim() || "http://localhost:3000";
|
|
11
|
+
const viteModuleProxyEntries = loadViteDevProxyEntries({
|
|
12
|
+
appRootUrl: import.meta.url,
|
|
13
|
+
fallbackTarget: apiProxyTarget
|
|
14
|
+
});
|
|
15
|
+
const clientEntry = (() => {
|
|
16
|
+
const normalized = String(process.env.VITE_CLIENT_ENTRY || "").trim();
|
|
17
|
+
if (!normalized) {
|
|
18
|
+
return "/src/main.js";
|
|
19
|
+
}
|
|
20
|
+
if (normalized.startsWith("/")) {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
if (normalized.startsWith("src/")) {
|
|
24
|
+
return `/${normalized}`;
|
|
25
|
+
}
|
|
26
|
+
return `/src/${normalized}`;
|
|
27
|
+
})();
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
resolve: {
|
|
31
|
+
preserveSymlinks: true,
|
|
32
|
+
alias: {
|
|
33
|
+
"@": fileURLToPath(new URL("./src", import.meta.url))
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
plugins: [
|
|
37
|
+
createJskitClientBootstrapPlugin(),
|
|
38
|
+
VueRouter({
|
|
39
|
+
routesFolder: "src/pages",
|
|
40
|
+
dts: "src/typed-router.d.ts",
|
|
41
|
+
// nestedChildren deprecated: JSKIT now relies on native index/... nesting instead of route rewrites.
|
|
42
|
+
// beforeWriteFiles: reparentNestedChildrenToIndexOwners
|
|
43
|
+
}),
|
|
44
|
+
vue(),
|
|
45
|
+
vuetify({
|
|
46
|
+
autoImport: true
|
|
47
|
+
}),
|
|
48
|
+
{
|
|
49
|
+
name: "jskit-client-entry",
|
|
50
|
+
transformIndexHtml(source) {
|
|
51
|
+
return String(source || "")
|
|
52
|
+
.replace(/\/src\/%VITE_CLIENT_ENTRY%/g, clientEntry)
|
|
53
|
+
.replace(/\/src\/main\.js/g, clientEntry);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
test: {
|
|
58
|
+
include: ["tests/client/**/*.vitest.js"]
|
|
59
|
+
},
|
|
60
|
+
optimizeDeps: {
|
|
61
|
+
entries: [
|
|
62
|
+
"index.html",
|
|
63
|
+
"src/**/*.{js,ts,vue}"
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
server: {
|
|
67
|
+
port: devPort,
|
|
68
|
+
warmup: {
|
|
69
|
+
clientFiles: [
|
|
70
|
+
"src/main.{js,ts}",
|
|
71
|
+
"src/router/**/*.{js,ts}",
|
|
72
|
+
"src/pages/**/*.{js,ts,vue}",
|
|
73
|
+
"src/components/**/*.{js,ts,vue}"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
proxy: {
|
|
77
|
+
"/api": {
|
|
78
|
+
target: apiProxyTarget,
|
|
79
|
+
changeOrigin: true
|
|
80
|
+
},
|
|
81
|
+
...viteModuleProxyEntries
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const VITE_DEV_PROXY_CONFIG_RELATIVE_PATH = ".jskit/vite.dev.proxy.json";
|
|
6
|
+
|
|
7
|
+
function toPositiveInt(value, fallback) {
|
|
8
|
+
const parsed = Number.parseInt(String(value || "").trim(), 10);
|
|
9
|
+
if (Number.isInteger(parsed) && parsed > 0) {
|
|
10
|
+
return parsed;
|
|
11
|
+
}
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function loadViteDevProxyEntries({ appRootUrl = import.meta.url, fallbackTarget = "" } = {}) {
|
|
16
|
+
const appRootPath = fileURLToPath(new URL(".", appRootUrl));
|
|
17
|
+
const absoluteConfigPath = path.join(appRootPath, VITE_DEV_PROXY_CONFIG_RELATIVE_PATH);
|
|
18
|
+
if (!existsSync(absoluteConfigPath)) {
|
|
19
|
+
return Object.freeze({});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let source = {};
|
|
23
|
+
try {
|
|
24
|
+
source = JSON.parse(readFileSync(absoluteConfigPath, "utf8"));
|
|
25
|
+
} catch {
|
|
26
|
+
return Object.freeze({});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const entries = Array.isArray(source?.entries) ? source.entries : [];
|
|
30
|
+
const proxy = {};
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const record = entry && typeof entry === "object" && !Array.isArray(entry) ? entry : {};
|
|
33
|
+
const routePath = String(record.path || "").trim();
|
|
34
|
+
if (!routePath || !routePath.startsWith("/")) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const target = String(record.target || "").trim() || String(fallbackTarget || "").trim();
|
|
39
|
+
if (!target) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const proxyConfig = {
|
|
44
|
+
target
|
|
45
|
+
};
|
|
46
|
+
if (Object.hasOwn(record, "changeOrigin")) {
|
|
47
|
+
proxyConfig.changeOrigin = record.changeOrigin === true;
|
|
48
|
+
}
|
|
49
|
+
if (Object.hasOwn(record, "ws")) {
|
|
50
|
+
proxyConfig.ws = record.ws === true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
proxy[routePath] = proxyConfig;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Object.freeze(proxy);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { toPositiveInt, loadViteDevProxyEntries };
|