@allanfsouza/aether-sdk 2.4.2 → 2.4.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/dist/auth.d.ts +51 -39
- package/dist/auth.js +70 -33
- package/dist/database.d.ts +35 -68
- package/dist/database.js +72 -78
- package/dist/index.d.ts +6 -15
- package/dist/index.js +12 -23
- package/dist/storage.d.ts +25 -14
- package/dist/storage.js +42 -20
- package/package.json +2 -2
- package/src/auth.ts +88 -44
- package/src/database.ts +80 -81
- package/src/index.ts +27 -46
- package/src/storage.ts +47 -20
package/src/auth.ts
CHANGED
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
import type { AxiosInstance } from "axios";
|
|
3
3
|
import type { PlataformaClient } from "./index.js";
|
|
4
4
|
|
|
5
|
+
// Exportando a interface User para o App usar
|
|
6
|
+
export interface User {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
avatarUrl?: string;
|
|
11
|
+
role?: string;
|
|
12
|
+
aetherRole?: string;
|
|
13
|
+
planCode?: string;
|
|
14
|
+
emailVerified?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
export interface LoginResponse {
|
|
6
18
|
accessToken: string;
|
|
7
19
|
refreshToken: string;
|
|
8
|
-
user:
|
|
9
|
-
id: string;
|
|
10
|
-
name: string;
|
|
11
|
-
email: string;
|
|
12
|
-
avatarUrl?: string;
|
|
13
|
-
role?: string;
|
|
14
|
-
aetherRole?: string;
|
|
15
|
-
planCode?: string;
|
|
16
|
-
emailVerified?: boolean;
|
|
17
|
-
};
|
|
20
|
+
user: User;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface Session {
|
|
@@ -29,15 +32,18 @@ export class AuthModule {
|
|
|
29
32
|
private client: PlataformaClient;
|
|
30
33
|
private http: AxiosInstance;
|
|
31
34
|
private refreshToken: string | null = null;
|
|
35
|
+
// Armazena o user localmente para acesso rápido via getSession
|
|
36
|
+
private currentUser: User | null = null;
|
|
32
37
|
|
|
33
38
|
constructor(client: PlataformaClient, http: AxiosInstance) {
|
|
34
39
|
this.client = client;
|
|
35
40
|
this.http = http;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
// =================================================================
|
|
44
|
+
// MÉTODOS ORIGINAIS (MANTIDOS)
|
|
45
|
+
// =================================================================
|
|
46
|
+
|
|
41
47
|
async login(email: string, password: string): Promise<LoginResponse> {
|
|
42
48
|
try {
|
|
43
49
|
const { data } = await this.http.post<LoginResponse>("/auth/login", {
|
|
@@ -48,19 +54,18 @@ export class AuthModule {
|
|
|
48
54
|
if (data.accessToken) {
|
|
49
55
|
this.client.setToken(data.accessToken);
|
|
50
56
|
this.refreshToken = data.refreshToken;
|
|
57
|
+
this.currentUser = data.user;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
return data;
|
|
54
61
|
} catch (e) {
|
|
55
62
|
this.client.setToken(null);
|
|
56
63
|
this.refreshToken = null;
|
|
64
|
+
this.currentUser = null;
|
|
57
65
|
throw e;
|
|
58
66
|
}
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
/**
|
|
62
|
-
* Registrar novo usuário
|
|
63
|
-
*/
|
|
64
69
|
async register(credentials: {
|
|
65
70
|
name: string;
|
|
66
71
|
email: string;
|
|
@@ -68,15 +73,17 @@ export class AuthModule {
|
|
|
68
73
|
}) {
|
|
69
74
|
try {
|
|
70
75
|
const { data } = await this.http.post("/auth/register", credentials);
|
|
76
|
+
// Se o register já retornar token, configura:
|
|
77
|
+
if (data.accessToken) {
|
|
78
|
+
this.client.setToken(data.accessToken);
|
|
79
|
+
this.currentUser = data.user;
|
|
80
|
+
}
|
|
71
81
|
return data;
|
|
72
82
|
} catch (e) {
|
|
73
83
|
throw e;
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
|
|
77
|
-
/**
|
|
78
|
-
* Renovar access token usando refresh token
|
|
79
|
-
*/
|
|
80
87
|
async refresh(): Promise<{ accessToken: string }> {
|
|
81
88
|
if (!this.refreshToken) {
|
|
82
89
|
throw new Error("Nenhum refresh token disponível");
|
|
@@ -99,16 +106,10 @@ export class AuthModule {
|
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
|
|
102
|
-
/**
|
|
103
|
-
* Obter URL de autenticação do Google
|
|
104
|
-
*/
|
|
105
109
|
getGoogleAuthUrl(): string {
|
|
106
110
|
return `${this.client.apiUrl}/v1/auth/google`;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
/**
|
|
110
|
-
* Logout da sessão atual
|
|
111
|
-
*/
|
|
112
113
|
async logout(): Promise<void> {
|
|
113
114
|
if (this.refreshToken) {
|
|
114
115
|
try {
|
|
@@ -119,26 +120,21 @@ export class AuthModule {
|
|
|
119
120
|
// Ignora erro de logout
|
|
120
121
|
}
|
|
121
122
|
}
|
|
122
|
-
|
|
123
123
|
this.client.setToken(null);
|
|
124
124
|
this.refreshToken = null;
|
|
125
|
+
this.currentUser = null;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
/**
|
|
128
|
-
* Logout de todas as sessões
|
|
129
|
-
*/
|
|
130
128
|
async logoutAll(): Promise<void> {
|
|
131
129
|
try {
|
|
132
130
|
await this.http.post("/auth/logout-all");
|
|
133
131
|
} finally {
|
|
134
132
|
this.client.setToken(null);
|
|
135
133
|
this.refreshToken = null;
|
|
134
|
+
this.currentUser = null;
|
|
136
135
|
}
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
/**
|
|
140
|
-
* Listar sessões ativas
|
|
141
|
-
*/
|
|
142
138
|
async getSessions(): Promise<Session[]> {
|
|
143
139
|
const { data } = await this.http.get<{ sessions: Session[] }>(
|
|
144
140
|
"/auth/sessions"
|
|
@@ -146,17 +142,11 @@ export class AuthModule {
|
|
|
146
142
|
return data.sessions;
|
|
147
143
|
}
|
|
148
144
|
|
|
149
|
-
/**
|
|
150
|
-
* Esqueci minha senha
|
|
151
|
-
*/
|
|
152
145
|
async forgotPassword(email: string) {
|
|
153
146
|
const { data } = await this.http.post("/auth/forgot-password", { email });
|
|
154
147
|
return data;
|
|
155
148
|
}
|
|
156
149
|
|
|
157
|
-
/**
|
|
158
|
-
* Redefinir senha
|
|
159
|
-
*/
|
|
160
150
|
async resetPassword(token: string, newPassword: string) {
|
|
161
151
|
const { data } = await this.http.post("/auth/reset-password", {
|
|
162
152
|
token,
|
|
@@ -165,17 +155,71 @@ export class AuthModule {
|
|
|
165
155
|
return data;
|
|
166
156
|
}
|
|
167
157
|
|
|
168
|
-
/**
|
|
169
|
-
* Obter refresh token atual (para armazenamento)
|
|
170
|
-
*/
|
|
171
158
|
getRefreshToken(): string | null {
|
|
172
159
|
return this.refreshToken;
|
|
173
160
|
}
|
|
174
161
|
|
|
175
|
-
/**
|
|
176
|
-
* Definir refresh token (para restaurar sessão)
|
|
177
|
-
*/
|
|
178
162
|
setRefreshToken(token: string | null) {
|
|
179
163
|
this.refreshToken = token;
|
|
180
164
|
}
|
|
165
|
+
|
|
166
|
+
// =================================================================
|
|
167
|
+
// NOVOS MÉTODOS (COMPATIBILIDADE COM SHOWCASE / SUPABASE STYLE)
|
|
168
|
+
// =================================================================
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Alias para login, retornando { user, error } padrão do Showcase
|
|
172
|
+
*/
|
|
173
|
+
async signInWithPassword(credentials: { email: string; password: string }) {
|
|
174
|
+
try {
|
|
175
|
+
const data = await this.login(credentials.email, credentials.password);
|
|
176
|
+
return { user: data.user, error: null };
|
|
177
|
+
} catch (err: any) {
|
|
178
|
+
return { user: null, error: err.response?.data?.message || err.message };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Alias para register, retornando { user, error }
|
|
184
|
+
*/
|
|
185
|
+
async signUp(credentials: { email: string; password: string; data?: { name: string } }) {
|
|
186
|
+
try {
|
|
187
|
+
const data = await this.register({
|
|
188
|
+
email: credentials.email,
|
|
189
|
+
password: credentials.password,
|
|
190
|
+
name: credentials.data?.name || credentials.email.split('@')[0],
|
|
191
|
+
});
|
|
192
|
+
return { user: data.user, error: null };
|
|
193
|
+
} catch (err: any) {
|
|
194
|
+
return { user: null, error: err.response?.data?.message || err.message };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Alias para logout
|
|
200
|
+
*/
|
|
201
|
+
async signOut() {
|
|
202
|
+
await this.logout();
|
|
203
|
+
return { error: null };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Recupera a sessão atual (User + Token).
|
|
208
|
+
* Se não tiver logado, retorna null ou tenta validar o token.
|
|
209
|
+
*/
|
|
210
|
+
async getSession() {
|
|
211
|
+
const token = this.client.getToken();
|
|
212
|
+
if (!token) return null;
|
|
213
|
+
|
|
214
|
+
// Se já temos o user em memória, retorna.
|
|
215
|
+
// O ideal seria validar o token no backend (/auth/me), mas vamos simplificar.
|
|
216
|
+
if (this.currentUser) {
|
|
217
|
+
return {
|
|
218
|
+
user: this.currentUser,
|
|
219
|
+
access_token: token
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
181
225
|
}
|
package/src/database.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/database.ts
|
|
2
2
|
import type { AxiosInstance } from "axios";
|
|
3
3
|
import type { PlataformaClient } from "./index.js";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
// Ajuste para WebSocket funcionar no Browser (Vite) e Node
|
|
6
|
+
const WS = typeof window !== "undefined" ? window.WebSocket : (global as any).WebSocket || null;
|
|
5
7
|
|
|
6
8
|
// Tipo para a mensagem que recebemos do WebSocket
|
|
7
9
|
type WebSocketMessage<T = any> = {
|
|
@@ -10,7 +12,7 @@ type WebSocketMessage<T = any> = {
|
|
|
10
12
|
data: T;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
|
-
//
|
|
15
|
+
// Opções de Listagem Avançada
|
|
14
16
|
export type ListOptions<T> = {
|
|
15
17
|
filter?: Partial<T> | Record<string, any>; // Suporta operadores avançados
|
|
16
18
|
sort?: {
|
|
@@ -35,89 +37,56 @@ export class QueryBuilder<T> {
|
|
|
35
37
|
this.collectionRef = collectionRef;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
/**
|
|
39
|
-
* Adiciona um filtro de igualdade.
|
|
40
|
-
*/
|
|
41
40
|
eq(column: keyof T & string, value: any): this {
|
|
42
41
|
this.filter[column] = value;
|
|
43
42
|
return this;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
* Adiciona um filtro de desigualdade ($ne).
|
|
48
|
-
*/
|
|
49
45
|
neq(column: keyof T & string, value: any): this {
|
|
50
46
|
this.filter[column] = { ...this.filter[column], $ne: value };
|
|
51
47
|
return this;
|
|
52
48
|
}
|
|
53
49
|
|
|
54
|
-
/**
|
|
55
|
-
* Adiciona um filtro maior que ($gt).
|
|
56
|
-
*/
|
|
57
50
|
gt(column: keyof T & string, value: number | string): this {
|
|
58
51
|
this.filter[column] = { ...this.filter[column], $gt: value };
|
|
59
52
|
return this;
|
|
60
53
|
}
|
|
61
54
|
|
|
62
|
-
/**
|
|
63
|
-
* Adiciona um filtro maior ou igual ($gte).
|
|
64
|
-
*/
|
|
65
55
|
gte(column: keyof T & string, value: number | string): this {
|
|
66
56
|
this.filter[column] = { ...this.filter[column], $gte: value };
|
|
67
57
|
return this;
|
|
68
58
|
}
|
|
69
59
|
|
|
70
|
-
/**
|
|
71
|
-
* Adiciona um filtro menor que ($lt).
|
|
72
|
-
*/
|
|
73
60
|
lt(column: keyof T & string, value: number | string): this {
|
|
74
61
|
this.filter[column] = { ...this.filter[column], $lt: value };
|
|
75
62
|
return this;
|
|
76
63
|
}
|
|
77
64
|
|
|
78
|
-
/**
|
|
79
|
-
* Adiciona um filtro menor ou igual ($lte).
|
|
80
|
-
*/
|
|
81
65
|
lte(column: keyof T & string, value: number | string): this {
|
|
82
66
|
this.filter[column] = { ...this.filter[column], $lte: value };
|
|
83
67
|
return this;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
|
-
/**
|
|
87
|
-
* Adiciona um filtro LIKE ($like).
|
|
88
|
-
*/
|
|
89
70
|
like(column: keyof T & string, value: string): this {
|
|
90
71
|
this.filter[column] = { ...this.filter[column], $like: value };
|
|
91
72
|
return this;
|
|
92
73
|
}
|
|
93
74
|
|
|
94
|
-
/**
|
|
95
|
-
* Define a ordenação.
|
|
96
|
-
*/
|
|
97
75
|
order(column: keyof T & string, direction: "ASC" | "DESC" = "ASC"): this {
|
|
98
76
|
this.sort = { field: column, order: direction };
|
|
99
77
|
return this;
|
|
100
78
|
}
|
|
101
79
|
|
|
102
|
-
/**
|
|
103
|
-
* Define o limite de registros.
|
|
104
|
-
*/
|
|
105
80
|
limit(count: number): this {
|
|
106
81
|
this.limitVal = count;
|
|
107
82
|
return this;
|
|
108
83
|
}
|
|
109
84
|
|
|
110
|
-
/**
|
|
111
|
-
* Define o deslocamento (paginação).
|
|
112
|
-
*/
|
|
113
85
|
offset(count: number): this {
|
|
114
86
|
this.offsetVal = count;
|
|
115
87
|
return this;
|
|
116
88
|
}
|
|
117
89
|
|
|
118
|
-
/**
|
|
119
|
-
* Executa a query e retorna os resultados.
|
|
120
|
-
*/
|
|
121
90
|
async get(): Promise<T[]> {
|
|
122
91
|
return this.collectionRef.list({
|
|
123
92
|
filter: this.filter,
|
|
@@ -151,15 +120,65 @@ export class DatabaseModule {
|
|
|
151
120
|
/**
|
|
152
121
|
* Seleciona uma coleção de dados.
|
|
153
122
|
* [PREMIUM] Suporta Generics <T> para tipagem forte.
|
|
154
|
-
*
|
|
123
|
+
* @example client.db.collection<Product>('products')
|
|
155
124
|
*/
|
|
156
125
|
collection<T = any>(collectionName: string) {
|
|
157
126
|
return new CollectionReference<T>(this.client, this.http, collectionName);
|
|
158
127
|
}
|
|
159
128
|
|
|
129
|
+
/**
|
|
130
|
+
* [NOVO] Método compatível com estilo 'Supabase/Drizzle'.
|
|
131
|
+
* É um alias para .collection() mas retorna interface compatível com o Showcase.
|
|
132
|
+
* @example client.db.from('posts').select('*')
|
|
133
|
+
*/
|
|
134
|
+
from<T = any>(tableName: string) {
|
|
135
|
+
const ref = this.collection<T>(tableName);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
// Mapeia .select() para .list()
|
|
139
|
+
select: async (columns = "*") => {
|
|
140
|
+
try {
|
|
141
|
+
// Nota: O parametro 'columns' poderia ser enviado ao backend se suportado
|
|
142
|
+
const data = await ref.list();
|
|
143
|
+
return { data, error: null };
|
|
144
|
+
} catch (err: any) {
|
|
145
|
+
return { data: null, error: err.response?.data || err.message };
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
// Mapeia .insert() para .create()
|
|
150
|
+
insert: async (data: T) => {
|
|
151
|
+
try {
|
|
152
|
+
const res = await ref.create(data);
|
|
153
|
+
return { data: res, error: null };
|
|
154
|
+
} catch (err: any) {
|
|
155
|
+
return { data: null, error: err.response?.data || err.message };
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Mapeia .update() para .update() (Requer ID no payload ou logica extra)
|
|
160
|
+
update: async (data: Partial<T> & { id?: string }) => {
|
|
161
|
+
try {
|
|
162
|
+
if (!data.id) throw new Error("ID é obrigatório para update via .from()");
|
|
163
|
+
const { id, ...updates } = data;
|
|
164
|
+
const res = await ref.update(id, updates as any);
|
|
165
|
+
return { data: res, error: null };
|
|
166
|
+
} catch (err: any) {
|
|
167
|
+
return { data: null, error: err.response?.data || err.message };
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// Mapeia .delete()
|
|
172
|
+
delete: async () => {
|
|
173
|
+
// O showcase não passou argumentos no delete, o que é perigoso.
|
|
174
|
+
// Aqui retornamos erro ou implementamos delete por query se suportado.
|
|
175
|
+
return { data: null, error: "Delete via .from() requer implementação de filtros" };
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
160
180
|
/**
|
|
161
181
|
* Executa múltiplas operações em uma única transação.
|
|
162
|
-
* Se uma falhar, todas são revertidas.
|
|
163
182
|
*/
|
|
164
183
|
async batch(operations: BatchOperation[]): Promise<any[]> {
|
|
165
184
|
const { data } = await this.http.post("/db/batch", { operations });
|
|
@@ -169,7 +188,6 @@ export class DatabaseModule {
|
|
|
169
188
|
|
|
170
189
|
/**
|
|
171
190
|
* Referência a uma coleção específica.
|
|
172
|
-
* O <T> define o formato dos dados (ex: interface User).
|
|
173
191
|
*/
|
|
174
192
|
export class CollectionReference<T> {
|
|
175
193
|
private client: PlataformaClient;
|
|
@@ -181,56 +199,41 @@ export class CollectionReference<T> {
|
|
|
181
199
|
this.client = client;
|
|
182
200
|
this.http = http;
|
|
183
201
|
this.collectionName = name;
|
|
202
|
+
|
|
184
203
|
// Ajusta protocolo para WS/WSS
|
|
185
204
|
const protocol = client.apiUrl.startsWith("https") ? "wss" : "ws";
|
|
186
205
|
this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
|
|
187
206
|
}
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
* Atalho para .eq()
|
|
192
|
-
*/
|
|
208
|
+
// --- Atalhos de Query ---
|
|
209
|
+
|
|
193
210
|
eq(column: keyof T & string, value: any): QueryBuilder<T> {
|
|
194
211
|
return new QueryBuilder<T>(this).eq(column, value);
|
|
195
212
|
}
|
|
196
213
|
|
|
197
|
-
/**
|
|
198
|
-
* Inicia o QueryBuilder.
|
|
199
|
-
* Atalho para .gt()
|
|
200
|
-
*/
|
|
201
214
|
gt(column: keyof T & string, value: number | string): QueryBuilder<T> {
|
|
202
215
|
return new QueryBuilder<T>(this).gt(column, value);
|
|
203
216
|
}
|
|
204
217
|
|
|
205
|
-
// ... Outros atalhos podem ser adicionados conforme necessidade ...
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Retorna uma nova instância do QueryBuilder.
|
|
209
|
-
*/
|
|
210
218
|
query(): QueryBuilder<T> {
|
|
211
219
|
return new QueryBuilder<T>(this);
|
|
212
220
|
}
|
|
213
221
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
* @param options Filtros e Ordenação
|
|
217
|
-
*/
|
|
222
|
+
// --- Operações CRUD ---
|
|
223
|
+
|
|
218
224
|
async list(options?: ListOptions<T>): Promise<T[]> {
|
|
219
225
|
const params: any = {};
|
|
220
226
|
|
|
221
|
-
// Converte os objetos do SDK para strings que o Backend entende
|
|
222
227
|
if (options?.filter) {
|
|
223
228
|
params.filter = JSON.stringify(options.filter);
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
if (options?.sort) {
|
|
227
|
-
// Backend espera formato array: ["campo", "DESC"]
|
|
228
232
|
params.sort = JSON.stringify([options.sort.field, options.sort.order]);
|
|
229
233
|
}
|
|
230
234
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// if (options?.offset) params.offset = options.offset;
|
|
235
|
+
if (options?.limit) params.limit = options.limit;
|
|
236
|
+
if (options?.offset) params.offset = options.offset;
|
|
234
237
|
|
|
235
238
|
const { data } = await this.http.get(`/db/${this.collectionName}`, {
|
|
236
239
|
params,
|
|
@@ -238,18 +241,11 @@ export class CollectionReference<T> {
|
|
|
238
241
|
return data.data;
|
|
239
242
|
}
|
|
240
243
|
|
|
241
|
-
/**
|
|
242
|
-
* Busca um documento pelo ID.
|
|
243
|
-
*/
|
|
244
244
|
async get(id: string): Promise<T> {
|
|
245
245
|
const { data } = await this.http.get(`/db/${this.collectionName}/${id}`);
|
|
246
246
|
return data.data;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
/**
|
|
250
|
-
* Cria um novo documento.
|
|
251
|
-
* O Partial<T> permite criar sem passar campos gerados (como id, createdAt).
|
|
252
|
-
*/
|
|
253
249
|
async create(newData: Partial<T>): Promise<T> {
|
|
254
250
|
const { data } = await this.http.post(
|
|
255
251
|
`/db/${this.collectionName}`,
|
|
@@ -258,9 +254,6 @@ export class CollectionReference<T> {
|
|
|
258
254
|
return data.data;
|
|
259
255
|
}
|
|
260
256
|
|
|
261
|
-
/**
|
|
262
|
-
* Atualiza um documento existente.
|
|
263
|
-
*/
|
|
264
257
|
async update(id: string, updates: Partial<T>): Promise<T> {
|
|
265
258
|
const { data } = await this.http.put(
|
|
266
259
|
`/db/${this.collectionName}/${id}`,
|
|
@@ -269,21 +262,21 @@ export class CollectionReference<T> {
|
|
|
269
262
|
return data.data;
|
|
270
263
|
}
|
|
271
264
|
|
|
272
|
-
/**
|
|
273
|
-
* Deleta um documento.
|
|
274
|
-
*/
|
|
275
265
|
async delete(id: string): Promise<boolean> {
|
|
276
266
|
await this.http.delete(`/db/${this.collectionName}/${id}`);
|
|
277
267
|
return true;
|
|
278
268
|
}
|
|
279
269
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
* O callback recebe os dados já tipados como T.
|
|
283
|
-
*/
|
|
270
|
+
// --- Realtime ---
|
|
271
|
+
|
|
284
272
|
subscribe(
|
|
285
273
|
callback: (action: "create" | "update" | "delete", data: T) => void
|
|
286
274
|
) {
|
|
275
|
+
if (!WS) {
|
|
276
|
+
console.warn("[SDK] WebSocket não disponível neste ambiente.");
|
|
277
|
+
return () => { };
|
|
278
|
+
}
|
|
279
|
+
|
|
287
280
|
const token = this.client.getToken();
|
|
288
281
|
const projectId = this.client.projectId;
|
|
289
282
|
|
|
@@ -292,12 +285,15 @@ export class CollectionReference<T> {
|
|
|
292
285
|
return () => { };
|
|
293
286
|
}
|
|
294
287
|
|
|
288
|
+
// URL correta de subscribe
|
|
295
289
|
const url = `${this.wsUrl}/v1/db/subscribe/${this.collectionName}?token=${token}&projectId=${projectId}`;
|
|
296
290
|
|
|
297
291
|
let ws: WebSocket | null = null;
|
|
298
292
|
|
|
299
293
|
try {
|
|
300
|
-
ws = new
|
|
294
|
+
ws = new WS(url);
|
|
295
|
+
|
|
296
|
+
if (!ws) return () => { };
|
|
301
297
|
|
|
302
298
|
ws.onopen = () => {
|
|
303
299
|
// Conectado
|
|
@@ -315,9 +311,12 @@ export class CollectionReference<T> {
|
|
|
315
311
|
}
|
|
316
312
|
};
|
|
317
313
|
|
|
318
|
-
//
|
|
314
|
+
// Heartbeat
|
|
319
315
|
const pingInterval = setInterval(() => {
|
|
320
|
-
|
|
316
|
+
// [CORREÇÃO] Adicionada verificação explicita 'ws &&' para evitar erro 'possibly null'
|
|
317
|
+
if (ws && ws.readyState === 1) { // 1 = OPEN
|
|
318
|
+
ws.send("ping");
|
|
319
|
+
}
|
|
321
320
|
}, 30000);
|
|
322
321
|
|
|
323
322
|
return () => {
|
|
@@ -329,4 +328,4 @@ export class CollectionReference<T> {
|
|
|
329
328
|
return () => { };
|
|
330
329
|
}
|
|
331
330
|
}
|
|
332
|
-
}
|
|
331
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import type { AxiosInstance } from "axios";
|
|
3
3
|
import { createHttpClient } from "./http-client.js";
|
|
4
|
-
import { AuthModule } from "./auth.js";
|
|
4
|
+
import { AuthModule, User } from "./auth.js"; // Importando User
|
|
5
5
|
import { DatabaseModule } from "./database.js";
|
|
6
6
|
import { StorageModule } from "./storage.js";
|
|
7
7
|
import { FunctionsModule } from "./functions.js";
|
|
@@ -11,89 +11,70 @@ import { PushModule } from "./push.js";
|
|
|
11
11
|
* Configuração usada para criar o cliente principal da plataforma.
|
|
12
12
|
*/
|
|
13
13
|
export type ClientConfig = {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Suporte duplo para compatibilidade:
|
|
15
|
+
// apiUrl (nome original) OU baseUrl (nome novo do App)
|
|
16
|
+
apiUrl?: string;
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
|
|
19
|
+
// projectId (nome original) OU apiKey (nome novo do App)
|
|
20
|
+
projectId?: string;
|
|
21
|
+
apiKey?: string;
|
|
16
22
|
};
|
|
17
23
|
|
|
18
|
-
/**
|
|
19
|
-
* O cliente principal da Plataforma API (Aether).
|
|
20
|
-
* Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
|
|
21
|
-
*/
|
|
22
24
|
export class PlataformaClient {
|
|
23
|
-
// Módulos públicos disponíveis para o usuário do SDK
|
|
24
25
|
public auth: AuthModule;
|
|
25
26
|
public db: DatabaseModule;
|
|
26
27
|
public storage: StorageModule;
|
|
27
28
|
public functions: FunctionsModule;
|
|
28
29
|
public push: PushModule;
|
|
29
30
|
|
|
30
|
-
//
|
|
31
|
+
// Alias para 'db' que o showcase tenta usar como 'database'
|
|
32
|
+
public database: DatabaseModule;
|
|
33
|
+
|
|
31
34
|
public apiUrl: string;
|
|
32
35
|
public projectId: string;
|
|
33
36
|
|
|
34
|
-
// Infra interna
|
|
35
37
|
public http: AxiosInstance;
|
|
36
38
|
private _token: string | null = null;
|
|
37
39
|
|
|
38
40
|
constructor(config: ClientConfig) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
// Resolve URL (prioridade para baseUrl se existir, senão apiUrl)
|
|
42
|
+
const url = config.baseUrl || config.apiUrl;
|
|
43
|
+
const project = config.apiKey || config.projectId;
|
|
44
|
+
|
|
45
|
+
if (!url || !project) {
|
|
46
|
+
throw new Error("baseUrl (API URL) e apiKey (Project ID) são obrigatórios.");
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
this.
|
|
45
|
-
this.projectId = config.projectId;
|
|
49
|
+
this.apiUrl = url.replace(/\/+$/, "");
|
|
50
|
+
this.projectId = project;
|
|
46
51
|
|
|
47
|
-
// Inicializa o cliente HTTP (passando a própria instância)
|
|
48
52
|
this.http = createHttpClient(this);
|
|
49
53
|
|
|
50
|
-
// Inicializa
|
|
54
|
+
// Inicializa módulos
|
|
51
55
|
this.auth = new AuthModule(this, this.http);
|
|
52
56
|
this.db = new DatabaseModule(this, this.http);
|
|
53
57
|
this.storage = new StorageModule(this, this.http);
|
|
54
58
|
this.functions = new FunctionsModule(this, this.http);
|
|
55
59
|
this.push = new PushModule(this, this.http);
|
|
56
|
-
}
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
// Cria o alias que o Showcase App espera
|
|
62
|
+
this.database = this.db;
|
|
63
|
+
}
|
|
59
64
|
|
|
60
|
-
/**
|
|
61
|
-
* Armazena o token de autenticação em memória.
|
|
62
|
-
* Chamado automaticamente pelo AuthModule após login/registro.
|
|
63
|
-
*/
|
|
64
65
|
setToken(token: string | null) {
|
|
65
66
|
this._token = token;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
/**
|
|
69
|
-
* Recupera o token de autenticação atual.
|
|
70
|
-
* Usado pelo http-client para injetar o header Authorization.
|
|
71
|
-
*/
|
|
72
69
|
getToken(): string | null {
|
|
73
70
|
return this._token;
|
|
74
71
|
}
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
// ===== ERRORS =====
|
|
74
|
+
// ===== EXPORTS =====
|
|
80
75
|
export { AetherError } from "./errors.js";
|
|
81
|
-
|
|
82
|
-
// ===== AUTH =====
|
|
83
|
-
export type { LoginResponse, Session } from "./auth.js";
|
|
84
|
-
|
|
85
|
-
// ===== DATABASE =====
|
|
76
|
+
export type { LoginResponse, Session, User } from "./auth.js"; // User exportado!
|
|
86
77
|
export type { ListOptions } from "./database.js";
|
|
87
|
-
|
|
88
|
-
// ===== STORAGE =====
|
|
89
|
-
// Nota: storage.ts não declara tipos específicos além dos retornos inline
|
|
90
|
-
// Se necessário, tipos podem ser adicionados em storage.ts e re-exportados aqui
|
|
91
|
-
|
|
92
|
-
// ===== FUNCTIONS =====
|
|
93
|
-
// Nota: functions.ts usa genéricos inline
|
|
94
|
-
// Se necessário, tipos podem ser adicionados em functions.ts e re-exportados aqui
|
|
95
|
-
|
|
96
|
-
// ===== PUSH =====
|
|
97
78
|
export type {
|
|
98
79
|
PushPlatform,
|
|
99
80
|
PushEnvironment,
|
|
@@ -104,4 +85,4 @@ export type {
|
|
|
104
85
|
PushLogEntry,
|
|
105
86
|
ListPushLogsOptions,
|
|
106
87
|
PushStats,
|
|
107
|
-
} from "./push.js";
|
|
88
|
+
} from "./push.js";
|