generate-ui-cli 2.1.7 → 2.3.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/README.md +216 -27
- package/dist/commands/angular.js +535 -40
- package/dist/commands/generate.js +350 -20
- package/dist/commands/login.js +131 -42
- package/dist/commands/merge.js +201 -0
- package/dist/generate-ui/routes.gen.js +4 -0
- package/dist/generators/angular/feature.generator.js +3183 -583
- package/dist/generators/angular/menu.generator.js +165 -0
- package/dist/generators/angular/routes.generator.js +8 -3
- package/dist/generators/screen.generator.js +100 -5
- package/dist/generators/screen.merge.js +70 -15
- package/dist/index.js +88 -12
- package/dist/license/guard.js +2 -1
- package/dist/license/permissions.js +63 -9
- package/dist/license/token.js +38 -3
- package/dist/postinstall.js +47 -0
- package/dist/runtime/config.js +4 -0
- package/dist/runtime/logger.js +29 -0
- package/dist/runtime/project-config.js +64 -0
- package/package.json +1 -1
|
@@ -14,12 +14,15 @@ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.generateui');
|
|
|
14
14
|
const PERMISSIONS_PATH = path_1.default.join(CONFIG_DIR, 'permissions.json');
|
|
15
15
|
const DEV_OFFLINE_DAYS = 7;
|
|
16
16
|
const FREE_DEFAULT = {
|
|
17
|
-
plan: 'free',
|
|
18
17
|
features: {
|
|
19
18
|
intelligentGeneration: false,
|
|
20
19
|
safeRegeneration: false,
|
|
21
20
|
uiOverrides: false,
|
|
22
|
-
maxGenerations: 1
|
|
21
|
+
maxGenerations: -1
|
|
22
|
+
},
|
|
23
|
+
subscription: {
|
|
24
|
+
status: 'anonymous',
|
|
25
|
+
reason: 'Login required to unlock paid features.'
|
|
23
26
|
}
|
|
24
27
|
};
|
|
25
28
|
function ensureConfigDir() {
|
|
@@ -30,7 +33,7 @@ function readCache() {
|
|
|
30
33
|
return null;
|
|
31
34
|
try {
|
|
32
35
|
const parsed = JSON.parse(fs_1.default.readFileSync(PERMISSIONS_PATH, 'utf-8'));
|
|
33
|
-
if (!parsed.
|
|
36
|
+
if (!parsed.features || !parsed.subscription?.status)
|
|
34
37
|
return null;
|
|
35
38
|
return parsed;
|
|
36
39
|
}
|
|
@@ -52,9 +55,7 @@ function writeCache(response) {
|
|
|
52
55
|
async function fetchPermissions() {
|
|
53
56
|
const apiBase = (0, config_1.getApiBaseUrl)();
|
|
54
57
|
const token = (0, token_1.loadToken)();
|
|
55
|
-
const headers = {
|
|
56
|
-
'Content-Type': 'application/json'
|
|
57
|
-
};
|
|
58
|
+
const headers = {};
|
|
58
59
|
if (token?.accessToken) {
|
|
59
60
|
headers.Authorization = `Bearer ${token.accessToken}`;
|
|
60
61
|
}
|
|
@@ -65,10 +66,48 @@ async function fetchPermissions() {
|
|
|
65
66
|
if (!response.ok) {
|
|
66
67
|
throw new Error('Failed to fetch permissions');
|
|
67
68
|
}
|
|
68
|
-
const
|
|
69
|
+
const raw = (await response.json());
|
|
70
|
+
const data = normalizePermissions(raw);
|
|
69
71
|
writeCache(data);
|
|
70
72
|
return data;
|
|
71
73
|
}
|
|
74
|
+
function normalizePermissions(raw) {
|
|
75
|
+
const features = normalizeFeatures(raw?.features);
|
|
76
|
+
const subscription = normalizeSubscription(raw);
|
|
77
|
+
return { features, subscription };
|
|
78
|
+
}
|
|
79
|
+
function normalizeFeatures(raw) {
|
|
80
|
+
const fallback = FREE_DEFAULT.features;
|
|
81
|
+
return {
|
|
82
|
+
intelligentGeneration: typeof raw?.intelligentGeneration === 'boolean'
|
|
83
|
+
? raw.intelligentGeneration
|
|
84
|
+
: fallback.intelligentGeneration,
|
|
85
|
+
safeRegeneration: typeof raw?.safeRegeneration === 'boolean'
|
|
86
|
+
? raw.safeRegeneration
|
|
87
|
+
: fallback.safeRegeneration,
|
|
88
|
+
uiOverrides: typeof raw?.uiOverrides === 'boolean'
|
|
89
|
+
? raw.uiOverrides
|
|
90
|
+
: fallback.uiOverrides,
|
|
91
|
+
maxGenerations: typeof raw?.maxGenerations === 'number'
|
|
92
|
+
? raw.maxGenerations
|
|
93
|
+
: fallback.maxGenerations
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function normalizeSubscription(raw) {
|
|
97
|
+
const source = raw?.subscription ?? {};
|
|
98
|
+
const status = String(source?.status ??
|
|
99
|
+
(raw?.plan === 'dev' ? 'active' : 'inactive'));
|
|
100
|
+
const reasonValue = source?.reason;
|
|
101
|
+
const normalizedStatus = status.toLowerCase();
|
|
102
|
+
return {
|
|
103
|
+
status,
|
|
104
|
+
reason: typeof reasonValue === 'string' && reasonValue.trim().length
|
|
105
|
+
? reasonValue
|
|
106
|
+
: normalizedStatus === 'active'
|
|
107
|
+
? null
|
|
108
|
+
: `Assinatura inativa. Faça upgrade para o plano Dev: ${(0, config_1.getDevPlanUrl)()}`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
72
111
|
function cacheIsValid(cache) {
|
|
73
112
|
const expiresAt = new Date(cache.expiresAt).getTime();
|
|
74
113
|
if (Number.isNaN(expiresAt))
|
|
@@ -81,12 +120,27 @@ async function getPermissions() {
|
|
|
81
120
|
}
|
|
82
121
|
catch {
|
|
83
122
|
const tokenPresent = (0, token_1.tokenFileExists)();
|
|
123
|
+
const tokenState = (0, token_1.getTokenState)();
|
|
84
124
|
const cache = readCache();
|
|
85
125
|
if (cache && cacheIsValid(cache)) {
|
|
86
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
features: cache.features,
|
|
128
|
+
subscription: cache.subscription
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// If the user is logged in but the API is temporarily unavailable,
|
|
132
|
+
// fallback to the last known permissions to avoid blocking generation.
|
|
133
|
+
if (tokenPresent && cache) {
|
|
134
|
+
return {
|
|
135
|
+
features: cache.features,
|
|
136
|
+
subscription: cache.subscription
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (tokenState === 'expired') {
|
|
140
|
+
throw new Error(`Sua sessão expirou. Faça login novamente: ${(0, config_1.getWebAuthUrl)()}`);
|
|
87
141
|
}
|
|
88
142
|
if (tokenPresent) {
|
|
89
|
-
throw new Error('
|
|
143
|
+
throw new Error('Login concluído, mas não foi possível validar sua licença agora. Verifique sua conexão com a API e tente novamente.');
|
|
90
144
|
}
|
|
91
145
|
return FREE_DEFAULT;
|
|
92
146
|
}
|
package/dist/license/token.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.tokenFileExists = tokenFileExists;
|
|
7
7
|
exports.loadToken = loadToken;
|
|
8
|
+
exports.getTokenState = getTokenState;
|
|
8
9
|
exports.saveToken = saveToken;
|
|
9
10
|
exports.clearToken = clearToken;
|
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -25,16 +26,50 @@ function loadToken() {
|
|
|
25
26
|
const parsed = JSON.parse(fs_1.default.readFileSync(TOKEN_PATH, 'utf-8'));
|
|
26
27
|
if (!parsed.accessToken || !parsed.expiresAt)
|
|
27
28
|
return null;
|
|
28
|
-
const expiresAt =
|
|
29
|
-
if (
|
|
29
|
+
const expiresAt = normalizeExpiresAt(parsed.expiresAt);
|
|
30
|
+
if (!expiresAt || expiresAt <= Date.now()) {
|
|
30
31
|
return null;
|
|
31
32
|
}
|
|
32
|
-
return
|
|
33
|
+
return {
|
|
34
|
+
...parsed,
|
|
35
|
+
expiresAt: new Date(expiresAt).toISOString()
|
|
36
|
+
};
|
|
33
37
|
}
|
|
34
38
|
catch {
|
|
35
39
|
return null;
|
|
36
40
|
}
|
|
37
41
|
}
|
|
42
|
+
function getTokenState() {
|
|
43
|
+
if (!tokenFileExists())
|
|
44
|
+
return 'missing';
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(TOKEN_PATH, 'utf-8'));
|
|
47
|
+
if (!parsed?.accessToken || !parsed?.expiresAt)
|
|
48
|
+
return 'invalid';
|
|
49
|
+
const expiresAt = normalizeExpiresAt(parsed.expiresAt);
|
|
50
|
+
if (!expiresAt)
|
|
51
|
+
return 'invalid';
|
|
52
|
+
if (expiresAt <= Date.now())
|
|
53
|
+
return 'expired';
|
|
54
|
+
return 'valid';
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return 'invalid';
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function normalizeExpiresAt(value) {
|
|
61
|
+
const trimmed = String(value).trim();
|
|
62
|
+
if (!trimmed.length)
|
|
63
|
+
return null;
|
|
64
|
+
const asNumber = Number(trimmed);
|
|
65
|
+
if (Number.isFinite(asNumber)) {
|
|
66
|
+
return asNumber < 1e12 ? asNumber * 1000 : asNumber;
|
|
67
|
+
}
|
|
68
|
+
const parsed = new Date(trimmed).getTime();
|
|
69
|
+
if (Number.isNaN(parsed))
|
|
70
|
+
return null;
|
|
71
|
+
return parsed;
|
|
72
|
+
}
|
|
38
73
|
function saveToken(token) {
|
|
39
74
|
ensureConfigDir();
|
|
40
75
|
fs_1.default.writeFileSync(TOKEN_PATH, JSON.stringify(token, null, 2));
|
package/dist/postinstall.js
CHANGED
|
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
6
8
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
7
9
|
const TELEMETRY_URL = process.env.GENERATEUI_TELEMETRY_URL?.trim() ||
|
|
8
10
|
'https://generateuibackend-production.up.railway.app/events';
|
|
@@ -25,4 +27,49 @@ async function sendInstallEvent() {
|
|
|
25
27
|
// Postinstall must never block installation.
|
|
26
28
|
}
|
|
27
29
|
}
|
|
30
|
+
function seedProjectConfig() {
|
|
31
|
+
const isGlobalInstall = String(process.env.npm_config_global || '') === 'true';
|
|
32
|
+
if (isGlobalInstall)
|
|
33
|
+
return;
|
|
34
|
+
const initCwd = process.env.INIT_CWD;
|
|
35
|
+
if (!initCwd)
|
|
36
|
+
return;
|
|
37
|
+
const projectRoot = path_1.default.resolve(initCwd);
|
|
38
|
+
const packageJsonPath = path_1.default.join(projectRoot, 'package.json');
|
|
39
|
+
const srcAppPath = path_1.default.join(projectRoot, 'src', 'app');
|
|
40
|
+
if (!fs_1.default.existsSync(packageJsonPath) || !fs_1.default.existsSync(srcAppPath))
|
|
41
|
+
return;
|
|
42
|
+
const configPath = path_1.default.join(projectRoot, 'generateui-config.json');
|
|
43
|
+
if (fs_1.default.existsSync(configPath))
|
|
44
|
+
return;
|
|
45
|
+
const openApiCandidates = ['openapi.yaml', 'openapi.yml', 'openapi.json'];
|
|
46
|
+
const openapi = openApiCandidates.find(file => fs_1.default.existsSync(path_1.default.join(projectRoot, file))) || 'openapi.yaml';
|
|
47
|
+
const config = {
|
|
48
|
+
openapi,
|
|
49
|
+
schemas: 'src/generate-ui',
|
|
50
|
+
features: 'src/app/features',
|
|
51
|
+
appTitle: 'Store',
|
|
52
|
+
defaultRoute: '',
|
|
53
|
+
menu: {
|
|
54
|
+
autoInject: true
|
|
55
|
+
},
|
|
56
|
+
views: {
|
|
57
|
+
ProductsAdmin: 'cards'
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
try {
|
|
61
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
62
|
+
console.log(` Created ${path_1.default.basename(configPath)} in project root`);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Postinstall must never block installation.
|
|
66
|
+
}
|
|
67
|
+
}
|
|
28
68
|
void sendInstallEvent();
|
|
69
|
+
seedProjectConfig();
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log('👋 GenerateUI CLI installed');
|
|
72
|
+
console.log(' Quick start:');
|
|
73
|
+
console.log(' 1) generate-ui generate');
|
|
74
|
+
console.log(' Tip: customize screens in generate-ui/overlays and menu in generate-ui/menu.overrides.json');
|
|
75
|
+
console.log('');
|
package/dist/runtime/config.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getCliVersion = getCliVersion;
|
|
7
7
|
exports.getApiBaseUrl = getApiBaseUrl;
|
|
8
8
|
exports.getWebAuthUrl = getWebAuthUrl;
|
|
9
|
+
exports.getDevPlanUrl = getDevPlanUrl;
|
|
9
10
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
10
11
|
function getCliVersion() {
|
|
11
12
|
return package_json_1.default.version || '0.0.0';
|
|
@@ -16,3 +17,6 @@ function getApiBaseUrl() {
|
|
|
16
17
|
function getWebAuthUrl() {
|
|
17
18
|
return ('https://generateuibackend-production.up.railway.app');
|
|
18
19
|
}
|
|
20
|
+
function getDevPlanUrl() {
|
|
21
|
+
return 'https://generate-ui.com/plan/dev';
|
|
22
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setVerbose = setVerbose;
|
|
4
|
+
exports.isVerbose = isVerbose;
|
|
5
|
+
exports.logDebug = logDebug;
|
|
6
|
+
exports.logStep = logStep;
|
|
7
|
+
exports.logTip = logTip;
|
|
8
|
+
let verbose = false;
|
|
9
|
+
function setVerbose(value) {
|
|
10
|
+
verbose = value;
|
|
11
|
+
}
|
|
12
|
+
function isVerbose() {
|
|
13
|
+
return verbose;
|
|
14
|
+
}
|
|
15
|
+
function logDebug(message) {
|
|
16
|
+
if (!verbose)
|
|
17
|
+
return;
|
|
18
|
+
console.log(`🔎 ${message}`);
|
|
19
|
+
}
|
|
20
|
+
function logStep(message) {
|
|
21
|
+
if (!verbose)
|
|
22
|
+
return;
|
|
23
|
+
console.log(`🧭 ${message}`);
|
|
24
|
+
}
|
|
25
|
+
function logTip(message) {
|
|
26
|
+
if (!verbose)
|
|
27
|
+
return;
|
|
28
|
+
console.log(`💡 ${message}`);
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.findProjectConfig = findProjectConfig;
|
|
7
|
+
exports.pickConfiguredPath = pickConfiguredPath;
|
|
8
|
+
exports.resolveOptionalPath = resolveOptionalPath;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
function findProjectConfig(startDir) {
|
|
12
|
+
let dir = path_1.default.resolve(startDir);
|
|
13
|
+
const root = path_1.default.parse(dir).root;
|
|
14
|
+
while (true) {
|
|
15
|
+
const candidate = path_1.default.join(dir, 'generateui-config.json');
|
|
16
|
+
if (fs_1.default.existsSync(candidate)) {
|
|
17
|
+
const parsed = tryReadConfig(candidate);
|
|
18
|
+
if (parsed) {
|
|
19
|
+
return {
|
|
20
|
+
configPath: candidate,
|
|
21
|
+
config: parsed
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (dir === root) {
|
|
26
|
+
return {
|
|
27
|
+
configPath: null,
|
|
28
|
+
config: null
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
dir = path_1.default.dirname(dir);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function pickConfiguredPath(config, key) {
|
|
35
|
+
const scoped = config?.paths?.[key];
|
|
36
|
+
if (typeof scoped === 'string' && scoped.trim().length > 0) {
|
|
37
|
+
return scoped.trim();
|
|
38
|
+
}
|
|
39
|
+
const topLevel = config?.[key];
|
|
40
|
+
if (typeof topLevel === 'string' && topLevel.trim().length > 0) {
|
|
41
|
+
return topLevel.trim();
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function resolveOptionalPath(cliValue, configuredValue, configPath) {
|
|
46
|
+
if (cliValue && cliValue.trim().length > 0) {
|
|
47
|
+
return path_1.default.resolve(process.cwd(), cliValue);
|
|
48
|
+
}
|
|
49
|
+
if (configuredValue &&
|
|
50
|
+
configuredValue.trim().length > 0 &&
|
|
51
|
+
configPath) {
|
|
52
|
+
return path_1.default.resolve(path_1.default.dirname(configPath), configuredValue);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function tryReadConfig(configPath) {
|
|
57
|
+
try {
|
|
58
|
+
const raw = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
59
|
+
return JSON.parse(raw);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|