@kosdev-code/kos-ui-cli 2.0.12 → 2.0.15
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/lib/cli.mjs +46 -35
- package/src/lib/generators/cache/index.mjs +6 -0
- package/src/lib/generators/component/index.mjs +2 -1
- package/src/lib/generators/env/index.mjs +10 -9
- package/src/lib/generators/i18n/namespace.mjs +2 -1
- package/src/lib/generators/model/companion.mjs +2 -1
- package/src/lib/generators/model/container.mjs +2 -1
- package/src/lib/generators/model/context.mjs +2 -1
- package/src/lib/generators/model/hook.mjs +2 -1
- package/src/lib/generators/model/model.mjs +3 -1
- package/src/lib/generators/project/app.mjs +2 -1
- package/src/lib/generators/project/i18n.mjs +2 -1
- package/src/lib/generators/project/plugin.mjs +2 -1
- package/src/lib/generators/project/splash.mjs +2 -1
- package/src/lib/generators/project/theme.mjs +2 -1
- package/src/lib/plopfile.mjs +7 -4
- package/src/lib/utils/action-factory.mjs +9 -0
- package/src/lib/utils/cache.mjs +1 -1
- package/src/lib/utils/nx-context.mjs +31 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kosdev-code/kos-ui-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.15",
|
|
4
4
|
"bin": {
|
|
5
5
|
"kosui": "./src/lib/cli.mjs"
|
|
6
6
|
},
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"main": "./src/index.js",
|
|
21
21
|
"kos": {
|
|
22
22
|
"build": {
|
|
23
|
-
"gitHash": "
|
|
23
|
+
"gitHash": "094801c4dad84a87afcf8b6cbdefd9aa38bfaf78"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"publishConfig": {
|
package/src/lib/cli.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import figlet from "figlet";
|
|
3
3
|
import minimist from "minimist";
|
|
4
|
+
import nodePlop from "node-plop";
|
|
4
5
|
import path, { dirname } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { Plop, run } from "plop";
|
|
7
7
|
import { clearCache } from "./utils/cache.mjs";
|
|
8
8
|
import { getGeneratorMetadata } from "./utils/generator-loader.mjs";
|
|
9
9
|
|
|
10
|
-
// -- Step 1: Sanitize flags early and persist to env
|
|
11
10
|
const originalArgs = process.argv.slice(2);
|
|
12
11
|
const parsedArgs = minimist(originalArgs);
|
|
12
|
+
const command = parsedArgs._[0] || "help";
|
|
13
13
|
|
|
14
14
|
const customFlags = ["--no-cache", "--refresh"];
|
|
15
15
|
const hasNoCache = originalArgs.includes("--no-cache");
|
|
@@ -21,51 +21,62 @@ if (hasNoCache || hasRefresh) {
|
|
|
21
21
|
clearCache();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// -- Step 2: Strip custom flags from argv so Plop doesn't choke
|
|
25
|
-
process.argv = [
|
|
26
|
-
...process.argv.slice(0, 2),
|
|
27
|
-
...originalArgs.filter((arg) => !customFlags.includes(arg)),
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
// -- Step 3: Resolve generator
|
|
31
|
-
const command = parsedArgs._[0] || "help";
|
|
32
24
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
33
25
|
const configPath = path.join(__dirname, "plopfile.mjs");
|
|
34
26
|
|
|
35
27
|
const showBanner =
|
|
36
|
-
command !== "help" &&
|
|
37
|
-
command !== "env" &&
|
|
38
|
-
process.env.DISABLE_CLI_BANNER !== "true";
|
|
28
|
+
command !== "help" && process.env.DISABLE_CLI_BANNER !== "true";
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Plop.prepare(
|
|
43
|
-
{
|
|
44
|
-
cwd: parsedArgs.cwd,
|
|
45
|
-
configPath,
|
|
46
|
-
preload: parsedArgs.preload || [],
|
|
47
|
-
completion: parsedArgs.completion,
|
|
48
|
-
},
|
|
49
|
-
async (env) => {
|
|
50
|
-
if (command && command !== "help" && command !== "env") {
|
|
51
|
-
const meta = await getGeneratorMetadata(command);
|
|
52
|
-
console.log(`--- Running KOS Generator: ${meta?.name || command} ---`);
|
|
53
|
-
} else if (command === "env") {
|
|
54
|
-
console.warn("--- Discover Studio Environment Variables ---");
|
|
55
|
-
} else {
|
|
56
|
-
console.warn("--- KOS CLI Help ---");
|
|
57
|
-
}
|
|
30
|
+
async function launch() {
|
|
31
|
+
const plop = await nodePlop(configPath);
|
|
58
32
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
33
|
+
plop.setActionType("clearCache", (answers, config, plop) => {
|
|
34
|
+
clearCache();
|
|
35
|
+
return "[ok] CLI cache cleared successfully.";
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!command || command === "help") {
|
|
39
|
+
console.warn("--- KOS CLI Help ---");
|
|
40
|
+
plop.getGeneratorList().forEach((g) => {
|
|
41
|
+
console.log(`- ${g.name}: ${g.description}`);
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const generator = plop.getGenerator(command);
|
|
47
|
+
|
|
48
|
+
if (!generator) {
|
|
49
|
+
console.error(`[kos-cli] Generator "${command}" not found.`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const meta = await getGeneratorMetadata(command);
|
|
53
|
+
if (showBanner) {
|
|
54
|
+
console.log(`--- Running KOS Generator: ${meta?.name || command} ---`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const answers = await generator.runPrompts();
|
|
59
|
+
const results = await generator.runActions(answers);
|
|
60
|
+
|
|
61
|
+
console.log("[kos-cli] Generator completed successfully!");
|
|
62
|
+
results.changes.forEach((result) => {
|
|
63
|
+
console.log(result.path || result.message);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
results.failures.forEach((fail) => {
|
|
67
|
+
console.error(fail.error || fail.message);
|
|
68
|
+
});
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error("[kos-cli] Generator run failed:", err.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
if (showBanner) {
|
|
65
76
|
figlet("KOS CLI", function (err, data) {
|
|
66
77
|
if (err) {
|
|
67
78
|
console.error("Figlet failed:", err);
|
|
68
|
-
return launch();
|
|
79
|
+
return launch(); // even if figlet fails, run!
|
|
69
80
|
}
|
|
70
81
|
console.log(data);
|
|
71
82
|
launch();
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { clearCache } from "../../utils/cache.mjs";
|
|
2
2
|
|
|
3
|
+
export const metadata = {
|
|
4
|
+
name: "cache:clear",
|
|
5
|
+
description: "Clear the generator context cache",
|
|
6
|
+
invalidateCache: false,
|
|
7
|
+
requiresModels: false,
|
|
8
|
+
};
|
|
3
9
|
export default function registerCacheGenerators(plop) {
|
|
4
10
|
plop.setGenerator("cache:clear", {
|
|
5
11
|
description: "Clear the generator context cache",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/component/index.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { getAllProjects } from "../../utils/nx-context.mjs";
|
|
4
5
|
import { required } from "../../utils/validators.mjs";
|
|
@@ -43,6 +44,6 @@ export default async function (plop) {
|
|
|
43
44
|
choices: allProjects,
|
|
44
45
|
},
|
|
45
46
|
],
|
|
46
|
-
actions: (
|
|
47
|
+
actions: actionFactory("createComponent", metadata),
|
|
47
48
|
});
|
|
48
49
|
}
|
|
@@ -12,15 +12,16 @@ export const metadata = {
|
|
|
12
12
|
export default async function (plop) {
|
|
13
13
|
plop.setPrompt("directory", directoryPrompt);
|
|
14
14
|
|
|
15
|
-
const isWorkspace = await detectWorkspace();
|
|
16
|
-
if (!isWorkspace) {
|
|
17
|
-
console.warn(
|
|
18
|
-
"[kos-cli] Not inside an Nx workspace. Skipping env generator."
|
|
19
|
-
);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
plop.setActionType("findEnv", async function () {
|
|
16
|
+
console.log("[kos-cli] Discovering Studio Environment Variables...");
|
|
17
|
+
const isWorkspace = await detectWorkspace();
|
|
18
|
+
|
|
19
|
+
if (!isWorkspace) {
|
|
20
|
+
console.warn(
|
|
21
|
+
"[kos-cli] Not inside an Nx workspace. Skipping env generator."
|
|
22
|
+
);
|
|
23
|
+
return "Skipped: Not a workspace";
|
|
24
|
+
}
|
|
24
25
|
const studioHome = findStudioHome();
|
|
25
26
|
const javaHome = findJavaExecutable();
|
|
26
27
|
|
|
@@ -29,7 +30,7 @@ export default async function (plop) {
|
|
|
29
30
|
`# Path to the Java binary\nJAVA_CMD='${javaHome}'\n\n# Path to the kos installation\nKOS_INSTALL_PATH='${studioHome}'\n`
|
|
30
31
|
);
|
|
31
32
|
|
|
32
|
-
return
|
|
33
|
+
return `\nStudio Home: ${studioHome}\nJava Home: ${javaHome}`;
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
plop.setGenerator("env", {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// generators/i18n/namespace.mjs
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
4
5
|
import { getAllProjects, getProjectDetails } from "../../utils/nx-context.mjs";
|
|
5
6
|
import { required } from "../../utils/validators.mjs";
|
|
6
7
|
|
|
@@ -50,6 +51,6 @@ export default async function (plop) {
|
|
|
50
51
|
choices: allProjects,
|
|
51
52
|
},
|
|
52
53
|
],
|
|
53
|
-
actions: (
|
|
54
|
+
actions: actionFactory("addNamespace", metadata),
|
|
54
55
|
});
|
|
55
56
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/model/companion.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import {
|
|
4
5
|
getAllModels,
|
|
@@ -72,6 +73,6 @@ export default async function (plop) {
|
|
|
72
73
|
},
|
|
73
74
|
...MODEL_PROMPTS,
|
|
74
75
|
],
|
|
75
|
-
actions: (
|
|
76
|
+
actions: actionFactory("createCompanionModel", metadata),
|
|
76
77
|
});
|
|
77
78
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/model/container.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import {
|
|
4
5
|
getAllModels,
|
|
@@ -61,6 +62,6 @@ export default async function (plop) {
|
|
|
61
62
|
},
|
|
62
63
|
...MODEL_PROMPTS,
|
|
63
64
|
],
|
|
64
|
-
actions: (
|
|
65
|
+
actions: actionFactory("createContainer", metadata),
|
|
65
66
|
});
|
|
66
67
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/model/context.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { getAllModels, getAllProjects } from "../../utils/nx-context.mjs";
|
|
4
5
|
export const metadata = {
|
|
@@ -49,6 +50,6 @@ export default async function (plop) {
|
|
|
49
50
|
choices: allProjects,
|
|
50
51
|
},
|
|
51
52
|
],
|
|
52
|
-
actions: (
|
|
53
|
+
actions: actionFactory("createContext", metadata),
|
|
53
54
|
});
|
|
54
55
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/model/hook.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { getAllModels, getAllProjects } from "../../utils/nx-context.mjs";
|
|
4
5
|
export const metadata = {
|
|
@@ -49,6 +50,6 @@ export default async function (plop) {
|
|
|
49
50
|
choices: allProjects,
|
|
50
51
|
},
|
|
51
52
|
],
|
|
52
|
-
actions: (
|
|
53
|
+
actions: actionFactory("createHook", metadata),
|
|
53
54
|
});
|
|
54
55
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/model/model.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import {
|
|
4
5
|
getAllProjects,
|
|
@@ -58,6 +59,7 @@ export default async function (plop) {
|
|
|
58
59
|
},
|
|
59
60
|
...MODEL_PROMPTS,
|
|
60
61
|
],
|
|
61
|
-
|
|
62
|
+
|
|
63
|
+
actions: actionFactory("createModel", metadata),
|
|
62
64
|
});
|
|
63
65
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/project/app.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { required } from "../../utils/validators.mjs";
|
|
4
5
|
|
|
@@ -32,6 +33,6 @@ export default async function (plop) {
|
|
|
32
33
|
validate: required,
|
|
33
34
|
},
|
|
34
35
|
],
|
|
35
|
-
actions: (
|
|
36
|
+
actions: actionFactory("createUiProject", metadata),
|
|
36
37
|
});
|
|
37
38
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/i18n/index.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { required } from "../../utils/validators.mjs";
|
|
4
5
|
export const metadata = {
|
|
@@ -31,6 +32,6 @@ export default async function (plop) {
|
|
|
31
32
|
validate: required,
|
|
32
33
|
},
|
|
33
34
|
],
|
|
34
|
-
actions: (
|
|
35
|
+
actions: actionFactory("createI18nProject", metadata),
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/project/plugin.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { required } from "../../utils/validators.mjs";
|
|
4
5
|
export const metadata = {
|
|
@@ -31,6 +32,6 @@ export default async function (plop) {
|
|
|
31
32
|
validate: required,
|
|
32
33
|
},
|
|
33
34
|
],
|
|
34
|
-
actions: (
|
|
35
|
+
actions: actionFactory("createPluginProject", metadata),
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/project/splash.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { required } from "../../utils/validators.mjs";
|
|
4
5
|
|
|
@@ -32,6 +33,6 @@ export default async function (plop) {
|
|
|
32
33
|
validate: required,
|
|
33
34
|
},
|
|
34
35
|
],
|
|
35
|
-
actions: (
|
|
36
|
+
actions: actionFactory("createSplashProject", metadata),
|
|
36
37
|
});
|
|
37
38
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// generators/project/theme.mjs
|
|
2
|
+
import { actionFactory } from "../../utils/action-factory.mjs";
|
|
2
3
|
import { execute } from "../../utils/exec.mjs";
|
|
3
4
|
import { required } from "../../utils/validators.mjs";
|
|
4
5
|
export const metadata = {
|
|
@@ -31,6 +32,6 @@ export default async function (plop) {
|
|
|
31
32
|
validate: required,
|
|
32
33
|
},
|
|
33
34
|
],
|
|
34
|
-
actions: (
|
|
35
|
+
actions: actionFactory("createThemeProject", metadata),
|
|
35
36
|
});
|
|
36
37
|
}
|
package/src/lib/plopfile.mjs
CHANGED
|
@@ -21,7 +21,14 @@ import registerSplashProject from "./generators/project/splash.mjs";
|
|
|
21
21
|
import registerTheme from "./generators/project/theme.mjs";
|
|
22
22
|
import registerWorkspace from "./generators/workspace/index.mjs";
|
|
23
23
|
|
|
24
|
+
import { clearCache } from "./utils/cache.mjs";
|
|
25
|
+
|
|
24
26
|
export default async function (plop) {
|
|
27
|
+
plop.setActionType("clearCache", (answers, config, plop) => {
|
|
28
|
+
clearCache();
|
|
29
|
+
return "[ok] CLI cache cleared successfully.";
|
|
30
|
+
});
|
|
31
|
+
|
|
25
32
|
await registerWorkspace(plop);
|
|
26
33
|
|
|
27
34
|
const isWorkspace = await detectWorkspace();
|
|
@@ -30,16 +37,13 @@ export default async function (plop) {
|
|
|
30
37
|
return;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
|
-
// Register all generators
|
|
34
40
|
await registerKosModel(plop);
|
|
35
41
|
await registerHook(plop);
|
|
36
42
|
await registerCompanion(plop);
|
|
37
43
|
await registerContainer(plop);
|
|
38
44
|
await registerContext(plop);
|
|
39
|
-
|
|
40
45
|
await registerComponent(plop);
|
|
41
46
|
await registerPluginComponent(plop);
|
|
42
|
-
|
|
43
47
|
await registerTheme(plop);
|
|
44
48
|
await registerUiProject(plop);
|
|
45
49
|
await registerPluginProject(plop);
|
|
@@ -47,6 +51,5 @@ export default async function (plop) {
|
|
|
47
51
|
await registerI18n(plop);
|
|
48
52
|
await registerI18nNamespace(plop);
|
|
49
53
|
await registerEnv(plop);
|
|
50
|
-
|
|
51
54
|
await registerCacheGenerators(plop);
|
|
52
55
|
}
|
package/src/lib/utils/cache.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import path from "path";
|
|
|
6
6
|
|
|
7
7
|
// Default to project-local cache; can swap to global using `getGlobalCachePath()`
|
|
8
8
|
const CACHE_PATH = path.resolve(".nx/cli-cache.json");
|
|
9
|
-
const CACHE_TTL =
|
|
9
|
+
const CACHE_TTL = 600 * 1000;
|
|
10
10
|
const ARGS = process.argv;
|
|
11
11
|
const DISABLE_CACHE =
|
|
12
12
|
process.env.DISABLE_CACHE === "true" || process.env.REFRESH === "true";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// utils/nx-context.mjs
|
|
2
2
|
import { execSync } from "child_process";
|
|
3
|
-
import { existsSync } from "fs";
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { getCached, setCached } from "./cache.mjs";
|
|
@@ -64,26 +64,38 @@ export async function getPluginProjects() {
|
|
|
64
64
|
export async function getAllModels() {
|
|
65
65
|
const cached = getCached("allModels");
|
|
66
66
|
if (cached) return cached;
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
console.warn(`parsing projects for models...`);
|
|
68
|
+
const allProjects = await getAllProjects();
|
|
69
|
+
const models = await allProjects.reduce(async (acc, curr) => {
|
|
70
|
+
console.warn(`[kos-cli] Pulling modles from project: ${curr}`);
|
|
71
|
+
const project = await getProjectDetails(curr);
|
|
72
|
+
const list = await acc;
|
|
73
|
+
|
|
74
|
+
const kosConfigPath = path.join(project.root, ".kos.json");
|
|
75
|
+
|
|
76
|
+
if (!existsSync(kosConfigPath)) {
|
|
77
|
+
console.warn(
|
|
78
|
+
`[kos-cli] No .kos.json found in project ${curr}, skipping...`
|
|
79
|
+
);
|
|
80
|
+
return list;
|
|
81
|
+
}
|
|
82
|
+
const kosConfig = JSON.parse(readFileSync(kosConfigPath, "utf-8"));
|
|
83
|
+
if (kosConfig.models) {
|
|
84
|
+
Object.keys(kosConfig.models).forEach((model) => {
|
|
85
|
+
list.push({ model: model, project: project.name });
|
|
86
|
+
});
|
|
82
87
|
}
|
|
83
|
-
}
|
|
84
88
|
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
return list.sort((a, b) => {
|
|
90
|
+
if (a.project === b.project) {
|
|
91
|
+
return a.model.localeCompare(b.model);
|
|
92
|
+
} else {
|
|
93
|
+
return a.project.localeCompare(b.project);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}, []);
|
|
97
|
+
setCached("allModels", models);
|
|
98
|
+
return models;
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
export async function getProjectDetails(projectName) {
|