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.
@@ -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.plan || !parsed.features)
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 data = (await response.json());
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 { plan: cache.plan, features: cache.features };
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('Please reconnect to verify your license.');
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
  }
@@ -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 = new Date(parsed.expiresAt).getTime();
29
- if (Number.isNaN(expiresAt) || expiresAt <= Date.now()) {
29
+ const expiresAt = normalizeExpiresAt(parsed.expiresAt);
30
+ if (!expiresAt || expiresAt <= Date.now()) {
30
31
  return null;
31
32
  }
32
- return parsed;
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));
@@ -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('');
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generate-ui-cli",
3
- "version": "2.1.7",
3
+ "version": "2.3.0",
4
4
  "description": "Generate UI from OpenAPI",
5
5
  "license": "MIT",
6
6
  "repository": {