generate-ui-cli 1.0.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.
@@ -0,0 +1,45 @@
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.generateRoutes = generateRoutes;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function generateRoutes(routes, featuresRoot) {
10
+ const appRoot = path_1.default.resolve(featuresRoot, '..');
11
+ const out = path_1.default.join(appRoot, 'generated', 'routes.gen.ts');
12
+ fs_1.default.mkdirSync(path_1.default.dirname(out), { recursive: true });
13
+ const content = `
14
+ import { Routes } from '@angular/router'
15
+
16
+ export const generatedRoutes: Routes = [
17
+ ${routes
18
+ .flatMap(r => {
19
+ const base = ` {
20
+ path: '${r.path}',
21
+ loadComponent: () =>
22
+ import('../features/${r.folder}/${r.fileBase}.component')
23
+ .then(m => m.${r.component})
24
+ }`;
25
+ const pascal = toPascalCase(r.path);
26
+ if (pascal === r.path)
27
+ return [base];
28
+ const alias = ` {
29
+ path: '${pascal}',
30
+ loadComponent: () =>
31
+ import('../features/${r.folder}/${r.fileBase}.component')
32
+ .then(m => m.${r.component})
33
+ }`;
34
+ return [base, alias];
35
+ })
36
+ .join(',\n')}
37
+ ]
38
+ `;
39
+ fs_1.default.writeFileSync(out, content);
40
+ }
41
+ function toPascalCase(value) {
42
+ if (!value)
43
+ return value;
44
+ return value[0].toUpperCase() + value.slice(1);
45
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFields = generateFields;
4
+ function mapSchema(schema) {
5
+ if (!schema || !schema.properties)
6
+ return [];
7
+ return Object.entries(schema.properties).map(([name, prop]) => ({
8
+ name,
9
+ type: prop.type ?? 'string',
10
+ required: schema.required?.includes(name) ?? false,
11
+ label: null,
12
+ placeholder: null,
13
+ ui: prop.type === 'array' ? 'tags' : null,
14
+ options: prop.enum ?? null,
15
+ defaultValue: prop.default ?? null,
16
+ validations: []
17
+ }));
18
+ }
19
+ /**
20
+ * REGRA CRÍTICA:
21
+ * - Wrapper (ex: article, user) NÃO vira campo de formulário
22
+ * - Se houver apenas 1 propriedade object, ela é o wrapper
23
+ */
24
+ function generateFields(endpoint, api) {
25
+ const schema = endpoint.requestBody?.content?.['application/json']?.schema;
26
+ const unwrapped = unwrapSchema(schema);
27
+ const baseSchema = unwrapped ?? schema;
28
+ if (!baseSchema || !baseSchema.properties)
29
+ return [];
30
+ const method = String(endpoint.method || '').toLowerCase();
31
+ const entity = String(endpoint.operationId || '').replace(/^(Create|Update|Get|Delete)/, '');
32
+ let finalSchema = baseSchema;
33
+ if (method === 'post' && entity && api?.components?.schemas) {
34
+ const schemas = api.components.schemas;
35
+ const newSchema = unwrapSchema(schemas[`New${entity}`]) ??
36
+ schemas[`New${entity}`];
37
+ const updateSchema = unwrapSchema(schemas[`Update${entity}`]) ?? schemas[`Update${entity}`];
38
+ if (newSchema && updateSchema) {
39
+ finalSchema = mergeSchemas(newSchema, updateSchema);
40
+ }
41
+ else if (newSchema) {
42
+ finalSchema = newSchema;
43
+ }
44
+ }
45
+ return mapSchema(finalSchema);
46
+ }
47
+ function unwrapSchema(schema) {
48
+ if (!schema || !schema.properties)
49
+ return null;
50
+ const propertyNames = Object.keys(schema.properties);
51
+ if (propertyNames.length === 1 &&
52
+ schema.properties[propertyNames[0]]?.type === 'object') {
53
+ return schema.properties[propertyNames[0]];
54
+ }
55
+ return null;
56
+ }
57
+ function mergeSchemas(primary, secondary) {
58
+ const merged = {
59
+ type: 'object',
60
+ properties: {
61
+ ...(primary?.properties ?? {}),
62
+ ...(secondary?.properties ?? {})
63
+ },
64
+ required: Array.from(new Set([
65
+ ...(primary?.required ?? []),
66
+ ...(secondary?.required ?? [])
67
+ ]))
68
+ };
69
+ return merged;
70
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inferEntity = inferEntity;
4
+ function inferEntity(endpoint) {
5
+ const parts = endpoint.path
6
+ .split('/')
7
+ .filter(Boolean)
8
+ .filter(p => !p.startsWith('{'));
9
+ if (!parts.length)
10
+ return 'Unknown';
11
+ const raw = parts[0];
12
+ // remove plural simples (users -> user)
13
+ const singular = raw.endsWith('s')
14
+ ? raw.slice(0, -1)
15
+ : raw;
16
+ return capitalize(singular);
17
+ }
18
+ function capitalize(value) {
19
+ return value.charAt(0).toUpperCase() + value.slice(1);
20
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inferSubmitWrapper = inferSubmitWrapper;
4
+ function inferSubmitWrapper(endpoint) {
5
+ const schema = endpoint.requestBody?.content?.['application/json']?.schema;
6
+ if (!schema?.properties)
7
+ return null;
8
+ const keys = Object.keys(schema.properties);
9
+ if (keys.length === 1 &&
10
+ schema.properties[keys[0]]?.type === 'object') {
11
+ return keys[0];
12
+ }
13
+ return null;
14
+ }
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateScreen = generateScreen;
4
+ const form_generator_1 = require("./form.generator");
5
+ function inferScreen(method, hasInput) {
6
+ if (method === 'get') {
7
+ if (hasInput) {
8
+ return { type: 'form', mode: 'filter' };
9
+ }
10
+ return { type: 'view', mode: 'readonly' };
11
+ }
12
+ if (method === 'post') {
13
+ return { type: 'form', mode: 'create' };
14
+ }
15
+ if (method === 'put' || method === 'patch') {
16
+ return { type: 'form', mode: 'edit' };
17
+ }
18
+ return { type: 'view', mode: 'readonly' };
19
+ }
20
+ function inferActions(method, hasInput) {
21
+ if (method === 'get' && hasInput) {
22
+ return { primary: { type: 'submit', label: 'Buscar' } };
23
+ }
24
+ if (method === 'post') {
25
+ return { primary: { type: 'submit', label: 'Criar' } };
26
+ }
27
+ if (method === 'put' || method === 'patch') {
28
+ return { primary: { type: 'submit', label: 'Salvar' } };
29
+ }
30
+ return {};
31
+ }
32
+ function inferSubmitWrap(endpoint) {
33
+ const schema = endpoint.requestBody?.content?.['application/json']?.schema;
34
+ if (!schema?.properties)
35
+ return null;
36
+ const keys = Object.keys(schema.properties);
37
+ if (keys.length === 1)
38
+ return keys[0];
39
+ return null;
40
+ }
41
+ function generateScreen(endpoint, api) {
42
+ const fields = (0, form_generator_1.generateFields)(endpoint, api);
43
+ const method = endpoint.method.toLowerCase();
44
+ const baseUrl = api?.servers?.[0]?.url;
45
+ const queryParams = extractQueryParams(endpoint, api);
46
+ const pathParams = extractPathParams(endpoint.path, api);
47
+ const hasInput = fields.length > 0 ||
48
+ queryParams.length > 0 ||
49
+ pathParams.length > 0;
50
+ const openapiVersion = api?.info?.version || 'unknown';
51
+ const screenMeta = buildMeta(endpoint.operationId, 'api', openapiVersion);
52
+ return {
53
+ meta: screenMeta,
54
+ entity: endpoint.summary
55
+ ? String(endpoint.summary).trim()
56
+ : endpoint.operationId.replace(/^(Create|Update|Get)/, ''),
57
+ screen: inferScreen(method, hasInput),
58
+ description: endpoint.description ?? null,
59
+ api: {
60
+ operationId: endpoint.operationId,
61
+ endpoint: endpoint.path,
62
+ method,
63
+ baseUrl,
64
+ pathParams,
65
+ queryParams,
66
+ submit: inferSubmitWrap(endpoint)
67
+ ? { wrap: inferSubmitWrap(endpoint) }
68
+ : undefined
69
+ },
70
+ layout: { type: 'single' },
71
+ fields: decorateFields(fields, 'body', openapiVersion),
72
+ actions: inferActions(method, hasInput),
73
+ data: {}
74
+ };
75
+ }
76
+ function extractQueryParams(endpoint, api) {
77
+ const params = endpoint?.parameters ?? [];
78
+ return params
79
+ .filter((param) => param?.in === 'query')
80
+ .map((param) => ({
81
+ name: param.name,
82
+ type: param.schema?.type ?? 'string',
83
+ required: Boolean(param.required),
84
+ label: toLabel(param.name),
85
+ placeholder: param.schema?.example ?? null,
86
+ options: param.schema?.enum ?? null,
87
+ defaultValue: param.schema?.default ?? null,
88
+ hint: param.description ?? null,
89
+ meta: buildMeta(`query:${param.name}`, 'api', api?.info?.version || 'unknown')
90
+ }));
91
+ }
92
+ function extractPathParams(path, api) {
93
+ const params = [];
94
+ const regex = /{([^}]+)}/g;
95
+ let match = regex.exec(path);
96
+ while (match) {
97
+ params.push(match[1]);
98
+ match = regex.exec(path);
99
+ }
100
+ return params.map(name => ({
101
+ name,
102
+ type: 'string',
103
+ required: true,
104
+ label: toLabel(name),
105
+ placeholder: toLabel(name),
106
+ options: null,
107
+ defaultValue: null,
108
+ hint: null,
109
+ meta: buildMeta(`path:${name}`, 'api', api?.info?.version || 'unknown')
110
+ }));
111
+ }
112
+ function toLabel(value) {
113
+ return String(value)
114
+ .replace(/[_-]/g, ' ')
115
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
116
+ .replace(/\b\w/g, char => char.toUpperCase());
117
+ }
118
+ function buildMeta(id, source, openapiVersion) {
119
+ return {
120
+ id,
121
+ source,
122
+ lastChangedBy: source,
123
+ introducedBy: source,
124
+ openapiVersion,
125
+ autoAdded: false,
126
+ userRemoved: false
127
+ };
128
+ }
129
+ function decorateFields(fields, scope, openapiVersion) {
130
+ return fields.map(field => ({
131
+ ...field,
132
+ meta: field.meta || buildMeta(`${scope}:${field.name}`, 'api', openapiVersion)
133
+ }));
134
+ }
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeScreen = mergeScreen;
4
+ const PRESENTATION_KEYS = [
5
+ 'label',
6
+ 'placeholder',
7
+ 'hint',
8
+ 'info',
9
+ 'ui',
10
+ 'group',
11
+ 'hidden'
12
+ ];
13
+ function mergeScreen(nextScreen, overlay, prevGenerated, options) {
14
+ const debug = [];
15
+ if (!overlay) {
16
+ return { screen: normalizeScreen(nextScreen, options), debug };
17
+ }
18
+ const normalizedNext = normalizeScreen(nextScreen, options);
19
+ const normalizedOverlay = normalizeScreen(overlay, options, 'user');
20
+ const normalizedPrev = prevGenerated
21
+ ? normalizeScreen(prevGenerated, options)
22
+ : null;
23
+ const merged = {
24
+ ...normalizedNext,
25
+ entity: normalizedOverlay.entity ?? normalizedNext.entity,
26
+ screen: normalizedOverlay.screen ?? normalizedNext.screen,
27
+ layout: normalizedOverlay.layout ?? normalizedNext.layout,
28
+ actions: mergeActions(normalizedNext.actions, normalizedOverlay.actions)
29
+ };
30
+ merged.api = {
31
+ ...normalizedNext.api,
32
+ pathParams: mergeFieldList(normalizedNext.api?.pathParams ?? [], normalizedOverlay.api?.pathParams ?? [], normalizedPrev?.api?.pathParams ?? [], options, debug, 'path'),
33
+ queryParams: mergeFieldList(normalizedNext.api?.queryParams ?? [], normalizedOverlay.api?.queryParams ?? [], normalizedPrev?.api?.queryParams ?? [], options, debug, 'query')
34
+ };
35
+ merged.fields = mergeFieldList(normalizedNext.fields ?? [], normalizedOverlay.fields ?? [], normalizedPrev?.fields ?? [], options, debug, 'body');
36
+ merged.meta = mergeMeta(normalizedNext.meta, normalizedOverlay.meta, options.openapiVersion);
37
+ return { screen: merged, debug };
38
+ }
39
+ function mergeActions(nextActions, overlayActions) {
40
+ if (!overlayActions)
41
+ return nextActions;
42
+ const merged = { ...nextActions };
43
+ if (overlayActions.primary?.label) {
44
+ merged.primary = merged.primary || {};
45
+ merged.primary.label = overlayActions.primary.label;
46
+ }
47
+ return merged;
48
+ }
49
+ function mergeFieldList(nextFields, overlayFields, prevFields, options, debug, scope) {
50
+ const nextMap = indexById(nextFields, scope);
51
+ const overlayMap = indexById(overlayFields, scope);
52
+ const prevMap = indexById(prevFields, scope);
53
+ const result = [];
54
+ const overlayOrder = overlayFields.map(field => getId(field, scope));
55
+ const used = new Set();
56
+ for (const id of overlayOrder) {
57
+ const overlayField = overlayMap.get(id);
58
+ const nextField = nextMap.get(id);
59
+ const prevField = prevMap.get(id);
60
+ used.add(id);
61
+ if (!nextField) {
62
+ debug.push(`REMOVED_BY_API ${id}`);
63
+ continue;
64
+ }
65
+ if (overlayField?.meta?.userRemoved) {
66
+ result.push({
67
+ ...nextField,
68
+ hidden: true,
69
+ meta: {
70
+ ...mergeMeta(nextField.meta, overlayField.meta, options.openapiVersion),
71
+ userRemoved: true,
72
+ lastChangedBy: 'user'
73
+ }
74
+ });
75
+ debug.push(`PRESERVE_USER_REMOVED ${id}`);
76
+ continue;
77
+ }
78
+ result.push(mergeField(nextField, overlayField, prevField, options, debug));
79
+ }
80
+ for (const [id, nextField] of nextMap.entries()) {
81
+ if (used.has(id))
82
+ continue;
83
+ const prevField = prevMap.get(id);
84
+ if (prevField && !overlayMap.has(id)) {
85
+ result.push({
86
+ ...nextField,
87
+ hidden: true,
88
+ meta: {
89
+ ...mergeMeta(nextField.meta, prevField.meta, options.openapiVersion),
90
+ userRemoved: true,
91
+ lastChangedBy: 'user'
92
+ }
93
+ });
94
+ debug.push(`USER_REMOVED_TOMBSTONE ${id}`);
95
+ continue;
96
+ }
97
+ const autoAdded = !nextField.required;
98
+ result.push({
99
+ ...nextField,
100
+ hidden: autoAdded ? true : nextField.hidden,
101
+ meta: {
102
+ ...mergeMeta(nextField.meta, nextField.meta, options.openapiVersion),
103
+ autoAdded
104
+ }
105
+ });
106
+ debug.push(`ADDED_BY_API ${id}`);
107
+ }
108
+ return result;
109
+ }
110
+ function mergeField(nextField, overlayField, prevField, options, debug) {
111
+ const merged = { ...nextField };
112
+ const meta = mergeMeta(nextField.meta, overlayField?.meta, options.openapiVersion);
113
+ for (const key of PRESENTATION_KEYS) {
114
+ if (overlayField && overlayField[key] !== undefined) {
115
+ merged[key] = overlayField[key];
116
+ }
117
+ }
118
+ if (prevField && prevField.required !== nextField.required) {
119
+ if (nextField.required) {
120
+ merged.hidden = false;
121
+ debug.push(`OPTIONAL_TO_REQUIRED ${meta.id}`);
122
+ }
123
+ else {
124
+ debug.push(`REQUIRED_TO_OPTIONAL ${meta.id}`);
125
+ }
126
+ }
127
+ if (prevField && prevField.type !== nextField.type) {
128
+ merged.ui = undefined;
129
+ merged.options = nextField.options ?? null;
130
+ debug.push(`TYPE_CHANGED ${meta.id}`);
131
+ }
132
+ const prevEnum = Array.isArray(prevField?.options);
133
+ const nextEnum = Array.isArray(nextField?.options);
134
+ if (prevEnum && !nextEnum) {
135
+ merged.options = null;
136
+ debug.push(`ENUM_TO_STRING ${meta.id}`);
137
+ }
138
+ if (!prevEnum && nextEnum) {
139
+ merged.options = nextField.options;
140
+ debug.push(`STRING_TO_ENUM ${meta.id}`);
141
+ }
142
+ merged.meta = meta;
143
+ return merged;
144
+ }
145
+ function normalizeScreen(screen, options, fallbackSource = 'api') {
146
+ if (!screen)
147
+ return screen;
148
+ const openapiVersion = options.openapiVersion;
149
+ const meta = screen.meta || buildMeta(screen.api?.operationId || 'screen', fallbackSource, openapiVersion);
150
+ return {
151
+ ...screen,
152
+ meta,
153
+ fields: normalizeFieldList(screen.fields, 'body', fallbackSource, openapiVersion),
154
+ api: {
155
+ ...screen.api,
156
+ pathParams: normalizeFieldList(screen.api?.pathParams, 'path', fallbackSource, openapiVersion),
157
+ queryParams: normalizeFieldList(screen.api?.queryParams, 'query', fallbackSource, openapiVersion)
158
+ }
159
+ };
160
+ }
161
+ function normalizeFieldList(list, scope, fallbackSource, openapiVersion) {
162
+ if (!Array.isArray(list))
163
+ return [];
164
+ return list.map(field => ({
165
+ ...field,
166
+ meta: field.meta || buildMeta(`${scope}:${field.name}`, fallbackSource, openapiVersion)
167
+ }));
168
+ }
169
+ function indexById(fields, scope) {
170
+ const map = new Map();
171
+ for (const field of fields) {
172
+ map.set(getId(field, scope), field);
173
+ }
174
+ return map;
175
+ }
176
+ function getId(field, scope) {
177
+ return field?.meta?.id || `${scope}:${field?.name}`;
178
+ }
179
+ function mergeMeta(nextMeta, overlayMeta, openapiVersion) {
180
+ const base = {
181
+ ...(nextMeta || overlayMeta),
182
+ openapiVersion
183
+ };
184
+ if (overlayMeta) {
185
+ base.source = overlayMeta.source || base.source;
186
+ base.introducedBy = overlayMeta.introducedBy || base.introducedBy;
187
+ base.lastChangedBy = overlayMeta.lastChangedBy || base.lastChangedBy;
188
+ base.userRemoved = overlayMeta.userRemoved || base.userRemoved;
189
+ }
190
+ return base;
191
+ }
192
+ function buildMeta(id, source, openapiVersion) {
193
+ return {
194
+ id,
195
+ source,
196
+ lastChangedBy: source,
197
+ introducedBy: source,
198
+ openapiVersion,
199
+ autoAdded: false,
200
+ userRemoved: false
201
+ };
202
+ }
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const generate_1 = require("./commands/generate");
6
+ const angular_1 = require("./commands/angular");
7
+ const login_1 = require("./commands/login");
8
+ const config_1 = require("./runtime/config");
9
+ const program = new commander_1.Command();
10
+ program
11
+ .name('generate-ui')
12
+ .description('Generate UI from OpenAPI')
13
+ .version((0, config_1.getCliVersion)())
14
+ .option('--no-telemetry', 'Disable telemetry');
15
+ /**
16
+ * 1️⃣ OpenAPI → Screen schemas
17
+ */
18
+ program
19
+ .command('generate')
20
+ .description('Generate screen schemas from OpenAPI')
21
+ .requiredOption('-o, --openapi <path>', 'OpenAPI file')
22
+ .option('-d, --debug', 'Explain merge decisions')
23
+ .action(async (options) => {
24
+ const { telemetry } = program.opts();
25
+ try {
26
+ await (0, generate_1.generate)({
27
+ openapi: options.openapi,
28
+ debug: options.debug,
29
+ telemetryEnabled: telemetry,
30
+ telemetryCommand: 'generate'
31
+ });
32
+ }
33
+ catch (error) {
34
+ handleCliError(error);
35
+ }
36
+ });
37
+ /**
38
+ * 2️⃣ Screen schemas → Angular code
39
+ */
40
+ program
41
+ .command('angular')
42
+ .description('Generate Angular code from screen schemas')
43
+ .requiredOption('-s, --schemas <path>', 'Directory containing generate-ui (with overlays/)')
44
+ .requiredOption('-f, --features <path>', 'Angular features output directory')
45
+ .action(async (options) => {
46
+ const { telemetry } = program.opts();
47
+ try {
48
+ await (0, angular_1.angular)({
49
+ schemasPath: options.schemas,
50
+ featuresPath: options.features,
51
+ telemetryEnabled: telemetry
52
+ });
53
+ }
54
+ catch (error) {
55
+ handleCliError(error);
56
+ }
57
+ });
58
+ /**
59
+ * 3️⃣ Login (Dev plan)
60
+ */
61
+ program
62
+ .command('login')
63
+ .description('Login to unlock Dev features')
64
+ .action(async () => {
65
+ const { telemetry } = program.opts();
66
+ try {
67
+ await (0, login_1.login)({ telemetryEnabled: telemetry });
68
+ }
69
+ catch (error) {
70
+ handleCliError(error);
71
+ }
72
+ });
73
+ /**
74
+ * 4️⃣ Regenerate with safe merge
75
+ */
76
+ program
77
+ .command('regenerate')
78
+ .description('Regenerate screen schemas with safe merge')
79
+ .requiredOption('-o, --openapi <path>', 'OpenAPI file')
80
+ .option('-d, --debug', 'Explain merge decisions')
81
+ .action(async (options) => {
82
+ const { telemetry } = program.opts();
83
+ try {
84
+ await (0, generate_1.generate)({
85
+ openapi: options.openapi,
86
+ debug: options.debug,
87
+ telemetryEnabled: telemetry,
88
+ telemetryCommand: 'regenerate',
89
+ requireSafeRegeneration: true
90
+ });
91
+ }
92
+ catch (error) {
93
+ handleCliError(error);
94
+ }
95
+ });
96
+ function handleCliError(error) {
97
+ if (error instanceof Error) {
98
+ console.error(error.message.replace(/\\n/g, '\n'));
99
+ }
100
+ else {
101
+ console.error('Unexpected error');
102
+ }
103
+ process.exit(1);
104
+ }
105
+ program.parse();
@@ -0,0 +1,58 @@
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.loadDeviceIdentity = loadDeviceIdentity;
7
+ exports.saveDeviceIdentity = saveDeviceIdentity;
8
+ exports.incrementFreeGeneration = incrementFreeGeneration;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const crypto_1 = __importDefault(require("crypto"));
13
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.generateui');
14
+ const DEVICE_PATH = path_1.default.join(CONFIG_DIR, 'device.json');
15
+ function ensureConfigDir() {
16
+ fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
17
+ }
18
+ function newDeviceIdentity() {
19
+ const deviceId = typeof crypto_1.default.randomUUID === 'function'
20
+ ? crypto_1.default.randomUUID()
21
+ : crypto_1.default.randomBytes(16).toString('hex');
22
+ return {
23
+ deviceId,
24
+ createdAt: new Date().toISOString(),
25
+ freeGenerationsUsed: 0
26
+ };
27
+ }
28
+ function loadDeviceIdentity() {
29
+ ensureConfigDir();
30
+ if (!fs_1.default.existsSync(DEVICE_PATH)) {
31
+ const identity = newDeviceIdentity();
32
+ fs_1.default.writeFileSync(DEVICE_PATH, JSON.stringify(identity, null, 2));
33
+ return identity;
34
+ }
35
+ const raw = fs_1.default.readFileSync(DEVICE_PATH, 'utf-8');
36
+ try {
37
+ const parsed = JSON.parse(raw);
38
+ if (!parsed.deviceId) {
39
+ throw new Error('Invalid device identity');
40
+ }
41
+ return parsed;
42
+ }
43
+ catch {
44
+ const identity = newDeviceIdentity();
45
+ fs_1.default.writeFileSync(DEVICE_PATH, JSON.stringify(identity, null, 2));
46
+ return identity;
47
+ }
48
+ }
49
+ function saveDeviceIdentity(identity) {
50
+ ensureConfigDir();
51
+ fs_1.default.writeFileSync(DEVICE_PATH, JSON.stringify(identity, null, 2));
52
+ }
53
+ function incrementFreeGeneration() {
54
+ const identity = loadDeviceIdentity();
55
+ identity.freeGenerationsUsed += 1;
56
+ saveDeviceIdentity(identity);
57
+ return identity;
58
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireFeature = requireFeature;
4
+ function requireFeature(features, featureKey, reason) {
5
+ if (!features[featureKey]) {
6
+ const details = reason ? ` ${reason}` : '';
7
+ throw new Error(`Requires Dev plan.${details} Execute \`generate-ui login\` to continue.`);
8
+ }
9
+ }