@trendify/cli 0.1.4
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/.env.example +2 -0
- package/README.md +52 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +300 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/config/app-paths.d.ts +4 -0
- package/dist/config/app-paths.d.ts.map +1 -0
- package/dist/config/app-paths.js +5 -0
- package/dist/config/env.d.ts +14 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/env.js +58 -0
- package/dist/modules/auth/auth-service.d.ts +39 -0
- package/dist/modules/auth/auth-service.d.ts.map +1 -0
- package/dist/modules/auth/auth-service.js +288 -0
- package/dist/modules/auth/auth-storage.d.ts +11 -0
- package/dist/modules/auth/auth-storage.d.ts.map +1 -0
- package/dist/modules/auth/auth-storage.js +65 -0
- package/dist/modules/auth/auth-user.d.ts +3 -0
- package/dist/modules/auth/auth-user.d.ts.map +1 -0
- package/dist/modules/auth/auth-user.js +10 -0
- package/dist/modules/auth/page/login-page.d.ts +12 -0
- package/dist/modules/auth/page/login-page.d.ts.map +1 -0
- package/dist/modules/auth/page/login-page.js +22 -0
- package/dist/modules/discovery/components/discovery-step-header.d.ts +7 -0
- package/dist/modules/discovery/components/discovery-step-header.d.ts.map +1 -0
- package/dist/modules/discovery/components/discovery-step-header.js +5 -0
- package/dist/modules/discovery/page/discovery-page.d.ts +11 -0
- package/dist/modules/discovery/page/discovery-page.d.ts.map +1 -0
- package/dist/modules/discovery/page/discovery-page.js +58 -0
- package/dist/modules/profile/page/profile-page.d.ts +12 -0
- package/dist/modules/profile/page/profile-page.d.ts.map +1 -0
- package/dist/modules/profile/page/profile-page.js +180 -0
- package/dist/shared/components/action-menu-page.d.ts +13 -0
- package/dist/shared/components/action-menu-page.d.ts.map +1 -0
- package/dist/shared/components/action-menu-page.js +7 -0
- package/dist/shared/components/radio-select.d.ts +12 -0
- package/dist/shared/components/radio-select.d.ts.map +1 -0
- package/dist/shared/components/radio-select.js +16 -0
- package/dist/shared/components/step-header.d.ts +7 -0
- package/dist/shared/components/step-header.d.ts.map +1 -0
- package/dist/shared/components/step-header.js +5 -0
- package/dist/shared/components/text-field.d.ts +12 -0
- package/dist/shared/components/text-field.d.ts.map +1 -0
- package/dist/shared/components/text-field.js +6 -0
- package/dist/shared/template/app-logo.d.ts +2 -0
- package/dist/shared/template/app-logo.d.ts.map +1 -0
- package/dist/shared/template/app-logo.js +13 -0
- package/dist/shared/template/app-menu.d.ts +17 -0
- package/dist/shared/template/app-menu.d.ts.map +1 -0
- package/dist/shared/template/app-menu.js +85 -0
- package/dist/shared/template/app-shell.d.ts +12 -0
- package/dist/shared/template/app-shell.d.ts.map +1 -0
- package/dist/shared/template/app-shell.js +15 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +1 -0
- package/package.json +44 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Trendify CLI
|
|
2
|
+
|
|
3
|
+
CLI do Trendify feita com Ink. O pacote publicado no npm e `@trendify/cli`, mas o comando instalado globalmente continua sendo `trendify`.
|
|
4
|
+
|
|
5
|
+
## Instalacao
|
|
6
|
+
|
|
7
|
+
Publicacao do pacote:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g @trendify/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Depois da instalacao, o comando continua sendo:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
trendify --help
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Ambiente
|
|
20
|
+
|
|
21
|
+
Crie `apps/cli/.env` a partir de `apps/cli/.env.example` ou exporte estas variaveis no shell:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
SUPABASE_URL=
|
|
25
|
+
SUPABASE_PUBLISHABLE_DEFAULT_KEY=
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
A sessao autenticada fica persistida em `~/.trendify/auth/storage.json`.
|
|
29
|
+
|
|
30
|
+
## Desenvolvimento local
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm run dev --workspace apps/cli
|
|
34
|
+
npm run build --workspace apps/cli
|
|
35
|
+
npm run check-types --workspace apps/cli
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Release para npm
|
|
39
|
+
|
|
40
|
+
O fluxo oficial de publicacao fica na raiz do monorepo.
|
|
41
|
+
|
|
42
|
+
Se quiser guardar o token no projeto:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
cp .env.publish.example .env.publish
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Depois preencha `NPM_TOKEN` e rode:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
npm run release:cli -- patch
|
|
52
|
+
```
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"AAaA,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAkGF,wBAAgB,GAAG,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,EAAE,QAAQ,2CAyVzD"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text, useApp, useInput } from 'ink';
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { DiscoveryPage } from './modules/discovery/page/discovery-page.js';
|
|
5
|
+
import { getUserDisplayName } from './modules/auth/auth-user.js';
|
|
6
|
+
import { getAuthBootstrapResult } from './modules/auth/auth-service.js';
|
|
7
|
+
import { LoginPage } from './modules/auth/page/login-page.js';
|
|
8
|
+
import { ProfilePage } from './modules/profile/page/profile-page.js';
|
|
9
|
+
import { ActionMenuPage } from './shared/components/action-menu-page.js';
|
|
10
|
+
import { AppMenu } from './shared/template/app-menu.js';
|
|
11
|
+
import { AppShell } from './shared/template/app-shell.js';
|
|
12
|
+
const MENU_ITEMS = [
|
|
13
|
+
{
|
|
14
|
+
id: 'discovery',
|
|
15
|
+
titulo: 'Descobrir Temas',
|
|
16
|
+
comando: 'discovery',
|
|
17
|
+
aliases: ['descobrir', 'Descobrir', 'temas', 'Temas'],
|
|
18
|
+
descricao: 'Inicia o fluxo de descoberta e coleta o tema da busca.',
|
|
19
|
+
palavrasChave: ['tema', 'Tema', 'descoberta', 'Descoberta', 'pesquisa', 'Pesquisa', 'amplo', 'restrito'],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'profile',
|
|
23
|
+
titulo: 'Meu Perfil',
|
|
24
|
+
comando: 'profile',
|
|
25
|
+
aliases: ['perfil', 'Perfil', 'conta', 'Conta', 'usuário', 'usuario', 'Usuário', 'Usuario'],
|
|
26
|
+
descricao: 'Abre os fluxos de alteração de senha e display name.',
|
|
27
|
+
palavrasChave: [
|
|
28
|
+
'senha',
|
|
29
|
+
'Senha',
|
|
30
|
+
'display name',
|
|
31
|
+
'Display Name',
|
|
32
|
+
'nome',
|
|
33
|
+
'Nome',
|
|
34
|
+
'usuário',
|
|
35
|
+
'usuario',
|
|
36
|
+
'Supabase',
|
|
37
|
+
'supabase',
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'logout',
|
|
42
|
+
titulo: 'Encerrar Sessão',
|
|
43
|
+
comando: 'logout',
|
|
44
|
+
aliases: ['sair-da-conta', 'sair da conta', 'Sair da Conta', 'deslogar', 'Deslogar'],
|
|
45
|
+
descricao: 'Remove a sessão salva localmente e volta para a tela de login.',
|
|
46
|
+
palavrasChave: ['auth', 'login', 'Login', 'conta', 'Conta', 'sessão', 'sessao', 'Sessão', 'Sessao', 'Supabase'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'exit',
|
|
50
|
+
titulo: 'Sair',
|
|
51
|
+
comando: 'sair',
|
|
52
|
+
aliases: ['exit', 'quit', 'sair', 'Sair'],
|
|
53
|
+
descricao: 'Sai do sistema sem perder a sessão do usuário ativa.',
|
|
54
|
+
palavrasChave: [
|
|
55
|
+
'fechar',
|
|
56
|
+
'Fechar',
|
|
57
|
+
'encerrar',
|
|
58
|
+
'Encerrar',
|
|
59
|
+
'finalizar',
|
|
60
|
+
'Finalizar',
|
|
61
|
+
'sessão',
|
|
62
|
+
'sessao',
|
|
63
|
+
'usuário',
|
|
64
|
+
'usuario',
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
const LOGOUT_OPTIONS = [
|
|
69
|
+
{
|
|
70
|
+
value: 'cancel',
|
|
71
|
+
label: 'Voltar ao menu',
|
|
72
|
+
description: 'Cancela a saida da conta e retorna para os comandos.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
value: 'confirm',
|
|
76
|
+
label: 'Encerrar sessao',
|
|
77
|
+
description: 'Remove a sessao local e exige novo login para continuar.',
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
function formatScopeLabel(scope) {
|
|
81
|
+
return scope === 'amplo' ? 'mais ampla' : 'mais restrita';
|
|
82
|
+
}
|
|
83
|
+
function getAuthenticatedUserLabel(user) {
|
|
84
|
+
if (!user) {
|
|
85
|
+
return 'usuario autenticado';
|
|
86
|
+
}
|
|
87
|
+
return getUserDisplayName(user) ?? user.email ?? 'usuario autenticado';
|
|
88
|
+
}
|
|
89
|
+
function getWelcomeTitle(user) {
|
|
90
|
+
const displayName = user ? getUserDisplayName(user) : null;
|
|
91
|
+
if (displayName) {
|
|
92
|
+
return `Bem-vindo de volta, ${displayName}.`;
|
|
93
|
+
}
|
|
94
|
+
return 'Bem-vindo de volta.';
|
|
95
|
+
}
|
|
96
|
+
export function App({ appVersion, initialInput }) {
|
|
97
|
+
const { exit } = useApp();
|
|
98
|
+
const authBootstrap = useMemo(() => getAuthBootstrapResult(), []);
|
|
99
|
+
const authService = authBootstrap.ok ? authBootstrap.authService : null;
|
|
100
|
+
const sessionStorageFilePath = authService?.getSessionStorageFilePath() ?? '~/.trendify/auth/storage.json';
|
|
101
|
+
const [screen, setScreen] = useState('menu');
|
|
102
|
+
const [notification, setNotification] = useState(null);
|
|
103
|
+
const [notificationTone, setNotificationTone] = useState('success');
|
|
104
|
+
const [menuKey, setMenuKey] = useState(0);
|
|
105
|
+
const [authStatus, setAuthStatus] = useState(authBootstrap.ok ? 'booting' : 'config-error');
|
|
106
|
+
const [authUser, setAuthUser] = useState(null);
|
|
107
|
+
const [email, setEmail] = useState('');
|
|
108
|
+
const [password, setPassword] = useState('');
|
|
109
|
+
const [authBusy, setAuthBusy] = useState(false);
|
|
110
|
+
const [logoutSelectionIndex, setLogoutSelectionIndex] = useState(0);
|
|
111
|
+
useInput((input, key) => {
|
|
112
|
+
if (key.ctrl && input === 'c') {
|
|
113
|
+
exit();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
useInput((_input, key) => {
|
|
117
|
+
if (screen !== 'confirm-logout' || authBusy) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (key.escape) {
|
|
121
|
+
setScreen('menu');
|
|
122
|
+
setLogoutSelectionIndex(0);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (key.upArrow) {
|
|
126
|
+
setLogoutSelectionIndex((current) => (current === 0 ? LOGOUT_OPTIONS.length - 1 : current - 1));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (key.downArrow) {
|
|
130
|
+
setLogoutSelectionIndex((current) => (current === LOGOUT_OPTIONS.length - 1 ? 0 : current + 1));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (key.return) {
|
|
134
|
+
const option = LOGOUT_OPTIONS[logoutSelectionIndex];
|
|
135
|
+
if (option?.value === 'confirm') {
|
|
136
|
+
void handleLogout();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
setScreen('menu');
|
|
140
|
+
setLogoutSelectionIndex(0);
|
|
141
|
+
setNotification('Encerramento de sessao cancelado.');
|
|
142
|
+
setNotificationTone('info');
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (!authService) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
let active = true;
|
|
150
|
+
setAuthBusy(true);
|
|
151
|
+
const restoreSession = async () => {
|
|
152
|
+
const result = await authService.restoreSession();
|
|
153
|
+
if (!active) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (result.error) {
|
|
157
|
+
setNotification(result.error);
|
|
158
|
+
setNotificationTone('error');
|
|
159
|
+
}
|
|
160
|
+
if (result.data?.user) {
|
|
161
|
+
setAuthStatus('authenticated');
|
|
162
|
+
setAuthUser(result.data.user);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
setAuthStatus('unauthenticated');
|
|
166
|
+
setAuthUser(null);
|
|
167
|
+
}
|
|
168
|
+
setAuthBusy(false);
|
|
169
|
+
};
|
|
170
|
+
void restoreSession();
|
|
171
|
+
const { data: { subscription }, } = authService.onAuthStateChange((snapshot) => {
|
|
172
|
+
if (!active) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (snapshot.user) {
|
|
176
|
+
setAuthStatus('authenticated');
|
|
177
|
+
setAuthUser(snapshot.user);
|
|
178
|
+
setScreen((current) => (current === 'menu' || current === 'discovery' || current === 'profile' ? current : 'menu'));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setAuthStatus('unauthenticated');
|
|
182
|
+
setAuthUser(null);
|
|
183
|
+
setScreen('menu');
|
|
184
|
+
setMenuKey((current) => current + 1);
|
|
185
|
+
});
|
|
186
|
+
return () => {
|
|
187
|
+
active = false;
|
|
188
|
+
subscription.unsubscribe();
|
|
189
|
+
};
|
|
190
|
+
}, [authService]);
|
|
191
|
+
async function handleLogin() {
|
|
192
|
+
if (!authService || authBusy) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const trimmedEmail = email.trim();
|
|
196
|
+
if (!trimmedEmail || !password.trim()) {
|
|
197
|
+
setNotification('Preencha email e senha para continuar.');
|
|
198
|
+
setNotificationTone('error');
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
setAuthBusy(true);
|
|
202
|
+
setNotification(null);
|
|
203
|
+
const result = await authService.signInWithPassword(trimmedEmail, password);
|
|
204
|
+
setAuthBusy(false);
|
|
205
|
+
if (result.error || !result.data?.user) {
|
|
206
|
+
setPassword('');
|
|
207
|
+
setNotification(result.error ?? 'Nao foi possivel autenticar este usuario.');
|
|
208
|
+
setNotificationTone('error');
|
|
209
|
+
setAuthStatus('unauthenticated');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
setAuthStatus('authenticated');
|
|
213
|
+
setAuthUser(result.data.user);
|
|
214
|
+
setPassword('');
|
|
215
|
+
setNotification(`Sessao iniciada para ${result.data.user.email ?? trimmedEmail}.`);
|
|
216
|
+
setNotificationTone('success');
|
|
217
|
+
setScreen('menu');
|
|
218
|
+
setMenuKey((current) => current + 1);
|
|
219
|
+
}
|
|
220
|
+
async function handleLogout() {
|
|
221
|
+
if (!authService || authBusy) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
setAuthBusy(true);
|
|
225
|
+
const result = await authService.signOut();
|
|
226
|
+
setAuthBusy(false);
|
|
227
|
+
if (result.error) {
|
|
228
|
+
setNotification(result.error);
|
|
229
|
+
setNotificationTone('error');
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
setScreen('menu');
|
|
233
|
+
setEmail('');
|
|
234
|
+
setPassword('');
|
|
235
|
+
setMenuKey((current) => current + 1);
|
|
236
|
+
setLogoutSelectionIndex(0);
|
|
237
|
+
setNotification('Sessao local removida. Faca login novamente para continuar.');
|
|
238
|
+
setNotificationTone('success');
|
|
239
|
+
}
|
|
240
|
+
if (!authBootstrap.ok) {
|
|
241
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Configuracao do Supabase pendente.", subtitle: "Defina as variaveis de ambiente antes de usar a CLI.", notification: authBootstrap.error, notificationTone: "error", children: _jsx(Text, { children: "Crie um arquivo .env com base em .env.example e preencha as chaves do projeto Supabase." }) }));
|
|
242
|
+
}
|
|
243
|
+
if (authStatus === 'booting') {
|
|
244
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Validando sua sessao.", subtitle: "A CLI esta consultando o Supabase para restaurar uma sessao existente.", notification: notification, notificationTone: notificationTone, children: _jsx(Text, { dimColor: true, children: authBusy ? 'Aguarde alguns instantes...' : 'Pronto para autenticar.' }) }));
|
|
245
|
+
}
|
|
246
|
+
if (authStatus === 'unauthenticated') {
|
|
247
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Login obrigatorio.", subtitle: "Somente usuarios ja cadastrados no Supabase podem usar a CLI.", notification: notification, notificationTone: notificationTone, children: _jsx(LoginPage, { busy: authBusy, email: email, password: password, sessionStorageFilePath: sessionStorageFilePath, onEmailChange: setEmail, onPasswordChange: setPassword, onSubmit: () => {
|
|
248
|
+
void handleLogin();
|
|
249
|
+
} }) }));
|
|
250
|
+
}
|
|
251
|
+
if (screen === 'discovery') {
|
|
252
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Discovery", subtitle: `Sessao ativa para ${getAuthenticatedUserLabel(authUser)}.`, notification: notification, notificationTone: notificationTone, children: _jsx(DiscoveryPage, { onCancel: () => {
|
|
253
|
+
setScreen('menu');
|
|
254
|
+
setMenuKey((current) => current + 1);
|
|
255
|
+
}, onComplete: ({ theme, scope }) => {
|
|
256
|
+
setNotification(`Discovery finalizado: tema "${theme}" com abordagem ${formatScopeLabel(scope)}.`);
|
|
257
|
+
setNotificationTone('success');
|
|
258
|
+
setScreen('menu');
|
|
259
|
+
setMenuKey((current) => current + 1);
|
|
260
|
+
} }) }));
|
|
261
|
+
}
|
|
262
|
+
if (screen === 'profile' && authService && authUser) {
|
|
263
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Profile", subtitle: `Sessao ativa para ${getAuthenticatedUserLabel(authUser)}.`, notification: notification, notificationTone: notificationTone, children: _jsx(ProfilePage, { authService: authService, user: authUser, onBack: () => {
|
|
264
|
+
setScreen('menu');
|
|
265
|
+
setMenuKey((current) => current + 1);
|
|
266
|
+
}, onNotificationChange: (nextNotification, nextTone = 'success') => {
|
|
267
|
+
setNotification(nextNotification);
|
|
268
|
+
setNotificationTone(nextTone);
|
|
269
|
+
} }) }));
|
|
270
|
+
}
|
|
271
|
+
if (screen === 'confirm-logout') {
|
|
272
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: "Confirmar encerramento de sessao", subtitle: `Voce esta autenticado como ${getAuthenticatedUserLabel(authUser)}.`, notification: notification, notificationTone: notificationTone, children: _jsx(ActionMenuPage, { title: "Tem certeza que deseja encerrar a sessao atual?", subtitle: "Se confirmar, a CLI vai remover a sessao salva localmente e pedir login novamente.", options: LOGOUT_OPTIONS, selectedIndex: logoutSelectionIndex, hintText: "Use as setas para navegar. Enter confirma. Esc volta ao menu." }) }));
|
|
273
|
+
}
|
|
274
|
+
return (_jsx(AppShell, { appVersion: appVersion, title: getWelcomeTitle(authUser), subtitle: `Use a barra para abrir os comandos. Sessao ativa para ${getAuthenticatedUserLabel(authUser)}.`, notification: notification, notificationTone: notificationTone, children: _jsx(AppMenu, { initialInput: menuKey === 0 ? initialInput : '', items: MENU_ITEMS, onInputChange: () => {
|
|
275
|
+
if (notification) {
|
|
276
|
+
setNotification(null);
|
|
277
|
+
}
|
|
278
|
+
}, onSelect: (item) => {
|
|
279
|
+
if (item.id === 'logout') {
|
|
280
|
+
setNotification(null);
|
|
281
|
+
setNotificationTone('info');
|
|
282
|
+
setLogoutSelectionIndex(0);
|
|
283
|
+
setScreen('confirm-logout');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (item.id === 'profile') {
|
|
287
|
+
setNotification(null);
|
|
288
|
+
setNotificationTone('success');
|
|
289
|
+
setScreen('profile');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (item.id === 'exit') {
|
|
293
|
+
exit();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
setNotification(null);
|
|
297
|
+
setNotificationTone('success');
|
|
298
|
+
setScreen('discovery');
|
|
299
|
+
} }, menuKey) }));
|
|
300
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import { App } from './app.js';
|
|
5
|
+
import { APP_VERSION } from './version.js';
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const initialInput = args
|
|
8
|
+
.filter((arg) => !arg.startsWith('-'))
|
|
9
|
+
.join(' ')
|
|
10
|
+
.trim();
|
|
11
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
12
|
+
process.stdout.write(`CLI
|
|
13
|
+
|
|
14
|
+
Uso:
|
|
15
|
+
trendify [opcoes]
|
|
16
|
+
|
|
17
|
+
Opcoes:
|
|
18
|
+
-h, --help Mostra esta ajuda
|
|
19
|
+
-v, --version Mostra a versao instalada
|
|
20
|
+
`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
24
|
+
process.stdout.write(`${APP_VERSION}\n`);
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
console.clear();
|
|
28
|
+
render(_jsx(App, { appVersion: APP_VERSION, initialInput: initialInput }));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-paths.d.ts","sourceRoot":"","sources":["../../src/config/app-paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,iBAAiB,QAA+B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AACjE,eAAO,MAAM,0BAA0B,QAA0C,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export const TRENDIFY_HOME_DIR = join(homedir(), '.trendify');
|
|
4
|
+
export const TRENDIFY_AUTH_DIR = join(TRENDIFY_HOME_DIR, 'auth');
|
|
5
|
+
export const TRENDIFY_AUTH_STORAGE_FILE = join(TRENDIFY_AUTH_DIR, 'storage.json');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type CliEnv = {
|
|
2
|
+
readonly supabasePublishableDefaultKey: string;
|
|
3
|
+
readonly supabaseUrl: string;
|
|
4
|
+
};
|
|
5
|
+
export type EnvValidationResult = {
|
|
6
|
+
readonly ok: true;
|
|
7
|
+
readonly env: CliEnv;
|
|
8
|
+
} | {
|
|
9
|
+
readonly error: string;
|
|
10
|
+
readonly ok: false;
|
|
11
|
+
};
|
|
12
|
+
export declare function getCliEnvValidationResult(): EnvValidationResult;
|
|
13
|
+
export declare const CLI_APP_ROOT_DIRECTORY: string;
|
|
14
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,6BAA6B,EAAE,MAAM,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAsBN,wBAAgB,yBAAyB,IAAI,mBAAmB,CA+B/D;AAED,eAAO,MAAM,sBAAsB,QAAmB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { config as loadEnv } from 'dotenv';
|
|
5
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
6
|
+
const currentDirectory = dirname(currentFilePath);
|
|
7
|
+
const appRootDirectory = resolve(currentDirectory, '..', '..');
|
|
8
|
+
const workspaceRootDirectory = resolve(appRootDirectory, '..', '..');
|
|
9
|
+
const ENV_FILE_PATHS = [
|
|
10
|
+
resolve(process.cwd(), '.env'),
|
|
11
|
+
resolve(appRootDirectory, '.env'),
|
|
12
|
+
resolve(workspaceRootDirectory, '.env'),
|
|
13
|
+
];
|
|
14
|
+
let envLoaded = false;
|
|
15
|
+
function loadCliEnvFiles() {
|
|
16
|
+
if (envLoaded) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const envFilePath of ENV_FILE_PATHS) {
|
|
20
|
+
if (existsSync(envFilePath)) {
|
|
21
|
+
loadEnv({ override: false, path: envFilePath, quiet: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
envLoaded = true;
|
|
25
|
+
}
|
|
26
|
+
function readEnvValue(name) {
|
|
27
|
+
const value = process.env[name]?.trim();
|
|
28
|
+
return value ? value : undefined;
|
|
29
|
+
}
|
|
30
|
+
export function getCliEnvValidationResult() {
|
|
31
|
+
loadCliEnvFiles();
|
|
32
|
+
const supabaseUrl = readEnvValue('SUPABASE_URL');
|
|
33
|
+
const supabasePublishableDefaultKey = readEnvValue('SUPABASE_PUBLISHABLE_DEFAULT_KEY');
|
|
34
|
+
const missingVariables = [
|
|
35
|
+
!supabaseUrl ? 'SUPABASE_URL' : null,
|
|
36
|
+
!supabasePublishableDefaultKey ? 'SUPABASE_PUBLISHABLE_DEFAULT_KEY' : null,
|
|
37
|
+
].filter((value) => Boolean(value));
|
|
38
|
+
if (missingVariables.length > 0) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
error: `Defina ${missingVariables.join(' e ')} antes de iniciar a CLI.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!supabaseUrl || !supabasePublishableDefaultKey) {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
error: 'Nao foi possivel validar as variaveis do Supabase.',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
env: {
|
|
53
|
+
supabaseUrl,
|
|
54
|
+
supabasePublishableDefaultKey,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export const CLI_APP_ROOT_DIRECTORY = appRootDirectory;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type AuthSession, type AuthUser } from '@supabase/supabase-js';
|
|
2
|
+
import { type CliEnv } from '../../config/env.js';
|
|
3
|
+
export type AuthSnapshot = {
|
|
4
|
+
readonly session: AuthSession | null;
|
|
5
|
+
readonly user: AuthUser | null;
|
|
6
|
+
};
|
|
7
|
+
export type AuthActionResult<TData> = {
|
|
8
|
+
readonly data: TData | null;
|
|
9
|
+
readonly error: string | null;
|
|
10
|
+
readonly errorCode: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type AuthBootstrapResult = {
|
|
13
|
+
readonly authService: AuthService;
|
|
14
|
+
readonly env: CliEnv;
|
|
15
|
+
readonly ok: true;
|
|
16
|
+
} | {
|
|
17
|
+
readonly error: string;
|
|
18
|
+
readonly ok: false;
|
|
19
|
+
};
|
|
20
|
+
export declare class AuthService {
|
|
21
|
+
private readonly client;
|
|
22
|
+
private readonly storageFilePath;
|
|
23
|
+
static create(env: CliEnv): AuthService;
|
|
24
|
+
private constructor();
|
|
25
|
+
getSessionStorageFilePath(): string;
|
|
26
|
+
onAuthStateChange(listener: (snapshot: AuthSnapshot) => void): {
|
|
27
|
+
data: {
|
|
28
|
+
subscription: import("@supabase/supabase-js").Subscription;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
restoreSession(): Promise<AuthActionResult<AuthSnapshot>>;
|
|
32
|
+
signInWithPassword(email: string, password: string): Promise<AuthActionResult<AuthSnapshot>>;
|
|
33
|
+
signOut(): Promise<AuthActionResult<null>>;
|
|
34
|
+
updateDisplayName(displayName: string): Promise<AuthActionResult<AuthUser>>;
|
|
35
|
+
sendPasswordReauthenticationCode(): Promise<AuthActionResult<null>>;
|
|
36
|
+
updatePassword(password: string, nonce?: string): Promise<AuthActionResult<AuthUser>>;
|
|
37
|
+
}
|
|
38
|
+
export declare function getAuthBootstrapResult(): AuthBootstrapResult;
|
|
39
|
+
//# sourceMappingURL=auth-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,QAAQ,EAEd,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAK7E,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,KAAK,IAChC;IACE,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC,CAAC;AAEJ,MAAM,MAAM,mBAAmB,GAC3B;IACE,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAUN,qBAAa,WAAW;IAiBpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,eAAe;WAjBpB,MAAM,CAAC,GAAG,EAAE,MAAM;IAehC,OAAO;IAKA,yBAAyB,IAAI,MAAM;IAInC,iBAAiB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI;;;;;IAStD,cAAc,IAAI,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IA6DzD,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAwC5F,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IA0B1C,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAmE3E,gCAAgC,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IA0BnE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;CA0CnG;AAID,wBAAgB,sBAAsB,IAAI,mBAAmB,CAoB5D"}
|