@allanfsouza/aether-sdk 1.0.0 → 2.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.
package/dist/auth.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AxiosInstance } from "axios";
2
2
  import type { PlataformaClient } from "./index.js";
3
3
  /**
4
4
  * Módulo de Autenticação
5
- * Lida com login, registro e gerenciamento de token.
5
+ * Lida com login, registro, gerenciamento de token e recuperação de senha.
6
6
  */
7
7
  export declare class AuthModule {
8
8
  private client;
@@ -16,7 +16,7 @@ export declare class AuthModule {
16
16
  */
17
17
  login(email: string, password: string): Promise<any>;
18
18
  /**
19
- * Registra um novo usuário e já o loga.
19
+ * Registra um novo usuário e já realiza o login automaticamente.
20
20
  * @param credentials Nome, email e senha
21
21
  * @returns O objeto do usuário e o token
22
22
  */
@@ -26,7 +26,20 @@ export declare class AuthModule {
26
26
  password: string;
27
27
  }): Promise<any>;
28
28
  /**
29
- * Desconecta o usuário limpando o token do SDK.
29
+ * Solicita um e-mail de recuperação de senha.
30
+ * @param email O e-mail da conta
31
+ * @returns A resposta da API (mensagem de sucesso)
32
+ */
33
+ forgotPassword(email: string): Promise<any>;
34
+ /**
35
+ * Redefine a senha usando o token recebido por e-mail.
36
+ * @param token O código/token recebido
37
+ * @param newPassword A nova senha desejada
38
+ * @returns A resposta da API (mensagem de sucesso)
39
+ */
40
+ resetPassword(token: string, newPassword: string): Promise<any>;
41
+ /**
42
+ * Desconecta o usuário limpando o token da memória do SDK.
30
43
  */
31
44
  logout(): void;
32
45
  }
package/dist/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Módulo de Autenticação
3
- * Lida com login, registro e gerenciamento de token.
3
+ * Lida com login, registro, gerenciamento de token e recuperação de senha.
4
4
  */
5
5
  export class AuthModule {
6
6
  constructor(client, http) {
@@ -16,9 +16,9 @@ export class AuthModule {
16
16
  async login(email, password) {
17
17
  try {
18
18
  const { data } = await this.http.post("/auth/login", { email, password });
19
- // Salva o token DENTRO da instância do SDK
19
+ // Salva o token DENTRO da instância do SDK para uso futuro
20
20
  this.client.setToken(data.token);
21
- return data; // Retorna { user, token }
21
+ return data; // Retorna { token, user: { ... } }
22
22
  }
23
23
  catch (e) {
24
24
  this.client.setToken(null); // Limpa o token em caso de falha
@@ -26,14 +26,17 @@ export class AuthModule {
26
26
  }
27
27
  }
28
28
  /**
29
- * Registra um novo usuário e já o loga.
29
+ * Registra um novo usuário e já realiza o login automaticamente.
30
30
  * @param credentials Nome, email e senha
31
31
  * @returns O objeto do usuário e o token
32
32
  */
33
33
  async register(credentials) {
34
34
  try {
35
35
  const { data } = await this.http.post("/auth/register", credentials);
36
- this.client.setToken(data.token);
36
+ // Se o backend retornar o token no registro, já salvamos
37
+ if (data.token) {
38
+ this.client.setToken(data.token);
39
+ }
37
40
  return data;
38
41
  }
39
42
  catch (e) {
@@ -42,7 +45,29 @@ export class AuthModule {
42
45
  }
43
46
  }
44
47
  /**
45
- * Desconecta o usuário limpando o token do SDK.
48
+ * Solicita um e-mail de recuperação de senha.
49
+ * @param email O e-mail da conta
50
+ * @returns A resposta da API (mensagem de sucesso)
51
+ */
52
+ async forgotPassword(email) {
53
+ const { data } = await this.http.post("/auth/forgot-password", { email });
54
+ return data;
55
+ }
56
+ /**
57
+ * Redefine a senha usando o token recebido por e-mail.
58
+ * @param token O código/token recebido
59
+ * @param newPassword A nova senha desejada
60
+ * @returns A resposta da API (mensagem de sucesso)
61
+ */
62
+ async resetPassword(token, newPassword) {
63
+ const { data } = await this.http.post("/auth/reset-password", {
64
+ token,
65
+ newPassword,
66
+ });
67
+ return data;
68
+ }
69
+ /**
70
+ * Desconecta o usuário limpando o token da memória do SDK.
46
71
  */
47
72
  logout() {
48
73
  this.client.setToken(null);
@@ -1,59 +1,62 @@
1
1
  import type { AxiosInstance } from "axios";
2
2
  import type { PlataformaClient } from "./index.js";
3
+ export type ListOptions<T> = {
4
+ filter?: Partial<T>;
5
+ sort?: {
6
+ field: keyof T & string;
7
+ order: "ASC" | "DESC";
8
+ };
9
+ };
3
10
  /**
4
- * Módulo de Banco de Dados (Firestore-like)
5
- * Ponto de entrada para acessar as coleções.
11
+ * Módulo de Banco de Dados
6
12
  */
7
13
  export declare class DatabaseModule {
8
14
  private client;
9
15
  private http;
10
16
  constructor(client: PlataformaClient, http: AxiosInstance);
11
17
  /**
12
- * Seleciona uma coleção de dados (ex: 'tasks' ou 'posts').
13
- * @param collectionName O nome da coleção (ex: "tasks")
14
- * @returns Uma instância de CollectionReference para operar nela
18
+ * Seleciona uma coleção de dados.
19
+ * [PREMIUM] Suporta Generics <T> para tipagem forte.
20
+ * * @example client.db.collection<Product>('products')
15
21
  */
16
- collection(collectionName: string): CollectionReference;
22
+ collection<T = any>(collectionName: string): CollectionReference<T>;
17
23
  }
18
24
  /**
19
- * Representa uma referência a uma coleção específica no banco.
20
- * Contém os métodos CRUD (create, list, update, delete) e subscribe.
25
+ * Referência a uma coleção específica.
26
+ * O <T> define o formato dos dados (ex: interface User).
21
27
  */
22
- declare class CollectionReference {
28
+ declare class CollectionReference<T> {
23
29
  private client;
24
30
  private http;
25
31
  private collectionName;
26
32
  private wsUrl;
27
33
  constructor(client: PlataformaClient, http: AxiosInstance, name: string);
28
34
  /**
29
- * Lista todos os documentos da coleção.
30
- * (GET /v1/db/tasks)
35
+ * Lista documentos da coleção com filtros opcionais.
36
+ * @param options Filtros e Ordenação
31
37
  */
32
- list(): Promise<any>;
38
+ list(options?: ListOptions<T>): Promise<T[]>;
33
39
  /**
34
- * Cria um novo documento na coleção.
35
- * (POST /v1/db/tasks)
36
- * @param newData O objeto de dados a ser criado
40
+ * Busca um documento pelo ID.
37
41
  */
38
- create(newData: Record<string, any>): Promise<any>;
42
+ get(id: string): Promise<T>;
43
+ /**
44
+ * Cria um novo documento.
45
+ * O Partial<T> permite criar sem passar campos gerados (como id, createdAt).
46
+ */
47
+ create(newData: Partial<T>): Promise<T>;
39
48
  /**
40
49
  * Atualiza um documento existente.
41
- * (PUT /v1/db/tasks/:id)
42
- * @param id O ID do documento
43
- * @param updates Os campos a serem atualizados
44
50
  */
45
- update(id: string, updates: Record<string, any>): Promise<any>;
51
+ update(id: string, updates: Partial<T>): Promise<T>;
46
52
  /**
47
53
  * Deleta um documento.
48
- * (DELETE /v1/db/tasks/:id)
49
- * @param id O ID do documento
50
54
  */
51
- delete(id: string): Promise<any>;
55
+ delete(id: string): Promise<boolean>;
52
56
  /**
53
- * Inscreve-se para mudanças em tempo real na coleção.
54
- * @param callback A função que será chamada com (action, data)
55
- * @returns Uma função 'unsubscribe' para fechar a conexão
57
+ * Inscreve-se para mudanças em tempo real.
58
+ * O callback recebe os dados tipados como T.
56
59
  */
57
- subscribe(callback: (action: "create" | "update" | "delete", data: any) => void): () => void;
60
+ subscribe(callback: (action: "create" | "update" | "delete", data: T) => void): () => void;
58
61
  }
59
62
  export {};
package/dist/database.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import WebSocket from "ws";
2
2
  /**
3
- * Módulo de Banco de Dados (Firestore-like)
4
- * Ponto de entrada para acessar as coleções.
3
+ * Módulo de Banco de Dados
5
4
  */
6
5
  export class DatabaseModule {
7
6
  constructor(client, http) {
@@ -9,98 +8,119 @@ export class DatabaseModule {
9
8
  this.http = http;
10
9
  }
11
10
  /**
12
- * Seleciona uma coleção de dados (ex: 'tasks' ou 'posts').
13
- * @param collectionName O nome da coleção (ex: "tasks")
14
- * @returns Uma instância de CollectionReference para operar nela
11
+ * Seleciona uma coleção de dados.
12
+ * [PREMIUM] Suporta Generics <T> para tipagem forte.
13
+ * * @example client.db.collection<Product>('products')
15
14
  */
16
15
  collection(collectionName) {
17
16
  return new CollectionReference(this.client, this.http, collectionName);
18
17
  }
19
18
  }
20
19
  /**
21
- * Representa uma referência a uma coleção específica no banco.
22
- * Contém os métodos CRUD (create, list, update, delete) e subscribe.
20
+ * Referência a uma coleção específica.
21
+ * O <T> define o formato dos dados (ex: interface User).
23
22
  */
24
23
  class CollectionReference {
25
24
  constructor(client, http, name) {
26
25
  this.client = client;
27
26
  this.http = http;
28
27
  this.collectionName = name;
29
- // Constrói a URL do WebSocket (substituindo http por ws)
30
- this.wsUrl = client.apiUrl.replace(/^http/, "ws");
28
+ // Ajusta protocolo para WS/WSS
29
+ const protocol = client.apiUrl.startsWith("https") ? "wss" : "ws";
30
+ this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
31
31
  }
32
32
  /**
33
- * Lista todos os documentos da coleção.
34
- * (GET /v1/db/tasks)
33
+ * Lista documentos da coleção com filtros opcionais.
34
+ * @param options Filtros e Ordenação
35
35
  */
36
- async list() {
37
- const { data } = await this.http.get(`/db/${this.collectionName}`);
38
- return data.data; // Retorna o array de documentos
36
+ async list(options) {
37
+ const params = {};
38
+ // Converte os objetos do SDK para strings que o Backend entende
39
+ if (options?.filter) {
40
+ params.filter = JSON.stringify(options.filter);
41
+ }
42
+ if (options?.sort) {
43
+ // Backend espera formato array: ["campo", "DESC"]
44
+ params.sort = JSON.stringify([options.sort.field, options.sort.order]);
45
+ }
46
+ const { data } = await this.http.get(`/db/${this.collectionName}`, {
47
+ params,
48
+ });
49
+ return data.data;
50
+ }
51
+ /**
52
+ * Busca um documento pelo ID.
53
+ */
54
+ async get(id) {
55
+ const { data } = await this.http.get(`/db/${this.collectionName}/${id}`);
56
+ return data.data;
39
57
  }
40
58
  /**
41
- * Cria um novo documento na coleção.
42
- * (POST /v1/db/tasks)
43
- * @param newData O objeto de dados a ser criado
59
+ * Cria um novo documento.
60
+ * O Partial<T> permite criar sem passar campos gerados (como id, createdAt).
44
61
  */
45
62
  async create(newData) {
46
63
  const { data } = await this.http.post(`/db/${this.collectionName}`, newData);
47
- return data.data; // Retorna o documento criado
64
+ return data.data;
48
65
  }
49
66
  /**
50
67
  * Atualiza um documento existente.
51
- * (PUT /v1/db/tasks/:id)
52
- * @param id O ID do documento
53
- * @param updates Os campos a serem atualizados
54
68
  */
55
69
  async update(id, updates) {
56
70
  const { data } = await this.http.put(`/db/${this.collectionName}/${id}`, updates);
57
- return data.data; // Retorna o documento atualizado
71
+ return data.data;
58
72
  }
59
73
  /**
60
74
  * Deleta um documento.
61
- * (DELETE /v1/db/tasks/:id)
62
- * @param id O ID do documento
63
75
  */
64
76
  async delete(id) {
65
- const { data } = await this.http.delete(`/db/${this.collectionName}/${id}`);
66
- return data; // Retorna { ok: true, ... }
77
+ await this.http.delete(`/db/${this.collectionName}/${id}`);
78
+ return true;
67
79
  }
68
80
  /**
69
- * Inscreve-se para mudanças em tempo real na coleção.
70
- * @param callback A função que será chamada com (action, data)
71
- * @returns Uma função 'unsubscribe' para fechar a conexão
81
+ * Inscreve-se para mudanças em tempo real.
82
+ * O callback recebe os dados tipados como T.
72
83
  */
73
84
  subscribe(callback) {
74
85
  const token = this.client.getToken();
75
86
  const projectId = this.client.projectId;
76
87
  if (!token || !projectId) {
77
- throw new Error("Não é possível se inscrever sem um token e projectId.");
88
+ console.warn("[SDK] Realtime falhou: Token ou ProjectId ausentes.");
89
+ return () => { };
78
90
  }
79
- // Constrói a URL: ws://localhost:3000/v1/db/subscribe/tasks?token=...&projectId=...
80
91
  const url = `${this.wsUrl}/v1/db/subscribe/${this.collectionName}?token=${token}&projectId=${projectId}`;
81
- const ws = new WebSocket(url);
82
- ws.on("open", () => {
83
- console.log(`[SDK] Inscrito em tempo real na coleção '${this.collectionName}'`);
84
- });
85
- ws.on("message", (message) => {
86
- try {
87
- const payload = JSON.parse(message);
88
- // Chama o callback do usuário
89
- callback(payload.action, payload.data);
90
- }
91
- catch (e) {
92
- console.error("[SDK] Erro ao processar mensagem WS:", e);
93
- }
94
- });
95
- ws.on("close", () => {
96
- console.log(`[SDK] Desconectado da coleção '${this.collectionName}'`);
97
- });
98
- ws.on("error", (err) => {
99
- console.error("[SDK] Erro no WebSocket:", err.message);
100
- });
101
- // Retorna uma função que o usuário pode chamar para parar de "ouvir"
102
- return () => {
103
- ws.close();
104
- };
92
+ let ws = null;
93
+ try {
94
+ ws = new WebSocket(url);
95
+ ws.onopen = () => {
96
+ // Conectado
97
+ };
98
+ ws.onmessage = (event) => {
99
+ try {
100
+ const raw = event.data?.toString() || event.toString();
101
+ if (raw === "pong")
102
+ return;
103
+ const payload = JSON.parse(raw);
104
+ callback(payload.action, payload.data);
105
+ }
106
+ catch (e) {
107
+ // Erro silencioso de parse
108
+ }
109
+ };
110
+ // Mantém a conexão viva (Heartbeat)
111
+ const pingInterval = setInterval(() => {
112
+ if (ws?.readyState === WebSocket.OPEN)
113
+ ws.send("ping");
114
+ }, 30000);
115
+ return () => {
116
+ clearInterval(pingInterval);
117
+ if (ws)
118
+ ws.close();
119
+ };
120
+ }
121
+ catch (err) {
122
+ console.error("[SDK] Erro WS:", err);
123
+ return () => { };
124
+ }
105
125
  }
106
126
  }
@@ -0,0 +1,14 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import type { PlataformaClient } from "./index.js";
3
+ export declare class FunctionsModule {
4
+ private client;
5
+ private http;
6
+ constructor(client: PlataformaClient, http: AxiosInstance);
7
+ /**
8
+ * Chama uma função HTTP Serverless.
9
+ * @param functionName O nome da função (ou rota, ex: 'pedidos/123')
10
+ * @param body (Opcional) Dados para enviar no corpo (POST)
11
+ * @param method (Opcional) Método HTTP (padrão POST se tiver body, GET se não)
12
+ */
13
+ invoke<T = any>(functionName: string, body?: any, method?: "GET" | "POST" | "PUT" | "DELETE"): Promise<T>;
14
+ }
@@ -0,0 +1,25 @@
1
+ export class FunctionsModule {
2
+ constructor(client, http) {
3
+ this.client = client;
4
+ this.http = http;
5
+ }
6
+ /**
7
+ * Chama uma função HTTP Serverless.
8
+ * @param functionName O nome da função (ou rota, ex: 'pedidos/123')
9
+ * @param body (Opcional) Dados para enviar no corpo (POST)
10
+ * @param method (Opcional) Método HTTP (padrão POST se tiver body, GET se não)
11
+ */
12
+ async invoke(functionName, body, method) {
13
+ const projectId = this.client.projectId;
14
+ // Remove barras iniciais para evitar URL malformada
15
+ const cleanName = functionName.replace(/^\//, "");
16
+ // Define método automaticamente se não informado
17
+ const finalMethod = method || (body ? "POST" : "GET");
18
+ const response = await this.http.request({
19
+ url: `/functions/http/${projectId}/${cleanName}`,
20
+ method: finalMethod,
21
+ data: body,
22
+ });
23
+ return response.data;
24
+ }
25
+ }
package/dist/index.d.ts CHANGED
@@ -1,18 +1,20 @@
1
1
  import { AuthModule } from "./auth.js";
2
2
  import { DatabaseModule } from "./database.js";
3
3
  import { StorageModule } from "./storage.js";
4
+ import { FunctionsModule } from "./functions.js";
4
5
  type ClientConfig = {
5
6
  apiUrl: string;
6
7
  projectId: string;
7
8
  };
8
9
  /**
9
10
  * O cliente principal da Plataforma API.
10
- * Ponto de entrada para todos os módulos (Auth, DB, Storage).
11
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
11
12
  */
12
13
  export declare class PlataformaClient {
13
14
  auth: AuthModule;
14
15
  db: DatabaseModule;
15
16
  storage: StorageModule;
17
+ functions: FunctionsModule;
16
18
  apiUrl: string;
17
19
  projectId: string;
18
20
  private http;
@@ -20,11 +22,13 @@ export declare class PlataformaClient {
20
22
  constructor(config: ClientConfig);
21
23
  /**
22
24
  * Armazena o token de autenticação em memória.
25
+ * Chamado automaticamente pelo AuthModule após login.
23
26
  * @param token O JWT (ou null para logout)
24
27
  */
25
28
  setToken(token: string | null): void;
26
29
  /**
27
30
  * Recupera o token de autenticação atual.
31
+ * Usado pelo http-client para injetar o header Authorization.
28
32
  * @returns O JWT ou null
29
33
  */
30
34
  getToken(): string | null;
package/dist/index.js CHANGED
@@ -2,9 +2,10 @@ import { createHttpClient } from "./http-client.js";
2
2
  import { AuthModule } from "./auth.js";
3
3
  import { DatabaseModule } from "./database.js";
4
4
  import { StorageModule } from "./storage.js";
5
+ import { FunctionsModule } from "./functions.js"; // [NOVO] Import do módulo
5
6
  /**
6
7
  * O cliente principal da Plataforma API.
7
- * Ponto de entrada para todos os módulos (Auth, DB, Storage).
8
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
8
9
  */
9
10
  export class PlataformaClient {
10
11
  constructor(config) {
@@ -12,18 +13,20 @@ export class PlataformaClient {
12
13
  if (!config.apiUrl || !config.projectId) {
13
14
  throw new Error("apiUrl e projectId são obrigatórios.");
14
15
  }
15
- this.apiUrl = config.apiUrl.replace(/\/$/, ""); // Remove barra final
16
+ this.apiUrl = config.apiUrl.replace(/\/$/, ""); // Remove barra final se houver
16
17
  this.projectId = config.projectId;
17
- // Inicializa o cliente HTTP (passando 'this', a própria instância)
18
+ // Inicializa o cliente HTTP (passando 'this', a própria instância para injetar token/ID)
18
19
  this.http = createHttpClient(this);
19
- // Inicializa os módulos
20
+ // Inicializa os módulos passando a referência do cliente e do axios
20
21
  this.auth = new AuthModule(this, this.http);
21
22
  this.db = new DatabaseModule(this, this.http);
22
23
  this.storage = new StorageModule(this, this.http);
24
+ this.functions = new FunctionsModule(this, this.http); // [NOVO] Inicialização
23
25
  }
24
26
  // --- Gerenciamento de Token ---
25
27
  /**
26
28
  * Armazena o token de autenticação em memória.
29
+ * Chamado automaticamente pelo AuthModule após login.
27
30
  * @param token O JWT (ou null para logout)
28
31
  */
29
32
  setToken(token) {
@@ -31,6 +34,7 @@ export class PlataformaClient {
31
34
  }
32
35
  /**
33
36
  * Recupera o token de autenticação atual.
37
+ * Usado pelo http-client para injetar o header Authorization.
34
38
  * @returns O JWT ou null
35
39
  */
36
40
  getToken() {
package/dist/storage.d.ts CHANGED
@@ -1,20 +1,28 @@
1
1
  import type { AxiosInstance } from "axios";
2
2
  import type { PlataformaClient } from "./index.js";
3
- /**
4
- * Módulo de Storage
5
- * Lida com upload e download de arquivos.
6
- */
7
3
  export declare class StorageModule {
8
4
  private client;
9
5
  private http;
10
6
  constructor(client: PlataformaClient, http: AxiosInstance);
11
7
  /**
12
- * Faz o upload de um arquivo para o Storage do projeto.
13
- * Isso lida com o fluxo de "presign" (URL assinada) automaticamente.
14
- * @param file O objeto 'File' (do navegador) ou um Buffer (do Node.js)
15
- * @param fileName O nome do arquivo (ex: "imagem.png")
16
- * @param contentType O tipo (ex: "image/png")
17
- * @returns O objeto do arquivo criado no banco
8
+ * Faz o upload de um arquivo.
9
+ * @param file Arquivo (Browser: File, Node: Buffer)
10
+ * @param fileName Nome do arquivo (ex: 'foto.jpg')
11
+ * @param contentType MIME Type (ex: 'image/jpeg')
12
+ * @param folder (Opcional) Pasta de destino (ex: 'usuarios/123/')
18
13
  */
19
- upload(file: File | Buffer, fileName: string, contentType: string): Promise<any>;
14
+ upload(file: File | Buffer | Blob, fileName: string, contentType: string, folder?: string): Promise<{
15
+ id: any;
16
+ key: any;
17
+ downloadUrl: any;
18
+ url: any;
19
+ }>;
20
+ /**
21
+ * Lista arquivos de uma pasta.
22
+ */
23
+ list(folder?: string): Promise<any>;
24
+ /**
25
+ * Deleta um arquivo pelo ID.
26
+ */
27
+ delete(fileId: string): Promise<boolean>;
20
28
  }
package/dist/storage.js CHANGED
@@ -1,40 +1,65 @@
1
- import axios from "axios"; // [A CORREÇÃO ESTÁ AQUI]
2
- /**
3
- * Módulo de Storage
4
- * Lida com upload e download de arquivos.
5
- */
1
+ import axios from "axios";
6
2
  export class StorageModule {
7
3
  constructor(client, http) {
8
4
  this.client = client;
9
5
  this.http = http;
10
6
  }
11
7
  /**
12
- * Faz o upload de um arquivo para o Storage do projeto.
13
- * Isso lida com o fluxo de "presign" (URL assinada) automaticamente.
14
- * @param file O objeto 'File' (do navegador) ou um Buffer (do Node.js)
15
- * @param fileName O nome do arquivo (ex: "imagem.png")
16
- * @param contentType O tipo (ex: "image/png")
17
- * @returns O objeto do arquivo criado no banco
8
+ * Faz o upload de um arquivo.
9
+ * @param file Arquivo (Browser: File, Node: Buffer)
10
+ * @param fileName Nome do arquivo (ex: 'foto.jpg')
11
+ * @param contentType MIME Type (ex: 'image/jpeg')
12
+ * @param folder (Opcional) Pasta de destino (ex: 'usuarios/123/')
18
13
  */
19
- async upload(file, fileName, contentType) {
20
- const size = file.size || file.length;
21
- // 1. Pedir a URL de upload para nossa API
14
+ async upload(file, fileName, contentType, folder // [NOVO]
15
+ ) {
16
+ // Calcula tamanho de forma segura para Browser e Node
17
+ let size = 0;
18
+ if (typeof File !== "undefined" && file instanceof File) {
19
+ size = file.size;
20
+ }
21
+ else if (typeof Blob !== "undefined" && file instanceof Blob) {
22
+ size = file.size;
23
+ }
24
+ else if (file instanceof Buffer) {
25
+ // Node.js
26
+ size = file.length;
27
+ }
28
+ // 1. Pedir URL assinada
22
29
  const { data: presignData } = await this.http.post("/storage/presign", {
23
- fileName: fileName,
24
- contentType: contentType,
25
- size: size,
30
+ fileName,
31
+ contentType,
32
+ size,
33
+ prefix: folder || "", // [NOVO] Envia o prefixo para o backend
26
34
  });
27
- // O 'presign' vem dentro de 'data'
28
35
  const presign = presignData.data;
29
- if (!presign?.url) {
30
- throw new Error("API não retornou uma URL de upload assinada.");
31
- }
32
- // 2. Enviar o arquivo diretamente para o Minio/S3 (sem auth)
33
- // Usamos o 'axios' global aqui, não o 'this.http' (que adicionaria auth)
36
+ if (!presign?.url)
37
+ throw new Error("Falha ao obter URL de upload.");
38
+ // 2. Upload direto para S3
34
39
  await axios.put(presign.url, file, {
35
40
  headers: { "Content-Type": contentType },
36
41
  });
37
- // 3. Retorna os detalhes do objeto (downloadUrl, objectId, etc.)
38
- return presign;
42
+ return {
43
+ id: presign.objectId,
44
+ key: presign.key,
45
+ downloadUrl: presign.downloadUrl,
46
+ url: presign.downloadUrl, // Alias amigável
47
+ };
48
+ }
49
+ /**
50
+ * Lista arquivos de uma pasta.
51
+ */
52
+ async list(folder = "") {
53
+ const { data } = await this.http.get("/storage/list", {
54
+ params: { projectId: this.client.projectId, prefix: folder },
55
+ });
56
+ return data.data; // Retorna { files: [], folders: [] }
57
+ }
58
+ /**
59
+ * Deleta um arquivo pelo ID.
60
+ */
61
+ async delete(fileId) {
62
+ await this.http.delete("/storage/delete", { data: { objectId: fileId } });
63
+ return true;
39
64
  }
40
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allanfsouza/aether-sdk",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "SDK do Cliente para a Plataforma API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/auth.ts CHANGED
@@ -4,7 +4,7 @@ import type { PlataformaClient } from "./index.js";
4
4
 
5
5
  /**
6
6
  * Módulo de Autenticação
7
- * Lida com login, registro e gerenciamento de token.
7
+ * Lida com login, registro, gerenciamento de token e recuperação de senha.
8
8
  */
9
9
  export class AuthModule {
10
10
  private client: PlataformaClient;
@@ -24,9 +24,9 @@ export class AuthModule {
24
24
  async login(email: string, password: string) {
25
25
  try {
26
26
  const { data } = await this.http.post("/auth/login", { email, password });
27
- // Salva o token DENTRO da instância do SDK
27
+ // Salva o token DENTRO da instância do SDK para uso futuro
28
28
  this.client.setToken(data.token);
29
- return data; // Retorna { user, token }
29
+ return data; // Retorna { token, user: { ... } }
30
30
  } catch (e: unknown) {
31
31
  this.client.setToken(null); // Limpa o token em caso de falha
32
32
  throw e;
@@ -34,7 +34,7 @@ export class AuthModule {
34
34
  }
35
35
 
36
36
  /**
37
- * Registra um novo usuário e já o loga.
37
+ * Registra um novo usuário e já realiza o login automaticamente.
38
38
  * @param credentials Nome, email e senha
39
39
  * @returns O objeto do usuário e o token
40
40
  */
@@ -45,7 +45,10 @@ export class AuthModule {
45
45
  }) {
46
46
  try {
47
47
  const { data } = await this.http.post("/auth/register", credentials);
48
- this.client.setToken(data.token);
48
+ // Se o backend retornar o token no registro, já salvamos
49
+ if (data.token) {
50
+ this.client.setToken(data.token);
51
+ }
49
52
  return data;
50
53
  } catch (e: unknown) {
51
54
  this.client.setToken(null);
@@ -54,7 +57,31 @@ export class AuthModule {
54
57
  }
55
58
 
56
59
  /**
57
- * Desconecta o usuário limpando o token do SDK.
60
+ * Solicita um e-mail de recuperação de senha.
61
+ * @param email O e-mail da conta
62
+ * @returns A resposta da API (mensagem de sucesso)
63
+ */
64
+ async forgotPassword(email: string) {
65
+ const { data } = await this.http.post("/auth/forgot-password", { email });
66
+ return data;
67
+ }
68
+
69
+ /**
70
+ * Redefine a senha usando o token recebido por e-mail.
71
+ * @param token O código/token recebido
72
+ * @param newPassword A nova senha desejada
73
+ * @returns A resposta da API (mensagem de sucesso)
74
+ */
75
+ async resetPassword(token: string, newPassword: string) {
76
+ const { data } = await this.http.post("/auth/reset-password", {
77
+ token,
78
+ newPassword,
79
+ });
80
+ return data;
81
+ }
82
+
83
+ /**
84
+ * Desconecta o usuário limpando o token da memória do SDK.
58
85
  */
59
86
  logout() {
60
87
  this.client.setToken(null);
package/src/database.ts CHANGED
@@ -4,15 +4,25 @@ import type { PlataformaClient } from "./index.js";
4
4
  import WebSocket from "ws";
5
5
 
6
6
  // Tipo para a mensagem que recebemos do WebSocket
7
- type WebSocketMessage = {
7
+ type WebSocketMessage<T = any> = {
8
8
  action: "create" | "update" | "delete";
9
9
  collection: string;
10
- data: any;
10
+ data: T;
11
+ };
12
+
13
+ // [NOVO] Opções de Listagem Avançada
14
+ export type ListOptions<T> = {
15
+ // Filtro parcial (ex: { status: 'active' })
16
+ filter?: Partial<T>;
17
+ // Ordenação
18
+ sort?: {
19
+ field: keyof T & string; // Garante que o campo existe no tipo T
20
+ order: "ASC" | "DESC";
21
+ };
11
22
  };
12
23
 
13
24
  /**
14
- * Módulo de Banco de Dados (Firestore-like)
15
- * Ponto de entrada para acessar as coleções.
25
+ * Módulo de Banco de Dados
16
26
  */
17
27
  export class DatabaseModule {
18
28
  private client: PlataformaClient;
@@ -24,20 +34,20 @@ export class DatabaseModule {
24
34
  }
25
35
 
26
36
  /**
27
- * Seleciona uma coleção de dados (ex: 'tasks' ou 'posts').
28
- * @param collectionName O nome da coleção (ex: "tasks")
29
- * @returns Uma instância de CollectionReference para operar nela
37
+ * Seleciona uma coleção de dados.
38
+ * [PREMIUM] Suporta Generics <T> para tipagem forte.
39
+ * * @example client.db.collection<Product>('products')
30
40
  */
31
- collection(collectionName: string) {
32
- return new CollectionReference(this.client, this.http, collectionName);
41
+ collection<T = any>(collectionName: string) {
42
+ return new CollectionReference<T>(this.client, this.http, collectionName);
33
43
  }
34
44
  }
35
45
 
36
46
  /**
37
- * Representa uma referência a uma coleção específica no banco.
38
- * Contém os métodos CRUD (create, list, update, delete) e subscribe.
47
+ * Referência a uma coleção específica.
48
+ * O <T> define o formato dos dados (ex: interface User).
39
49
  */
40
- class CollectionReference {
50
+ class CollectionReference<T> {
41
51
  private client: PlataformaClient;
42
52
  private http: AxiosInstance;
43
53
  private collectionName: string;
@@ -47,103 +57,123 @@ class CollectionReference {
47
57
  this.client = client;
48
58
  this.http = http;
49
59
  this.collectionName = name;
50
- // Constrói a URL do WebSocket (substituindo http por ws)
51
- this.wsUrl = client.apiUrl.replace(/^http/, "ws");
60
+ // Ajusta protocolo para WS/WSS
61
+ const protocol = client.apiUrl.startsWith("https") ? "wss" : "ws";
62
+ this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
63
+ }
64
+
65
+ /**
66
+ * Lista documentos da coleção com filtros opcionais.
67
+ * @param options Filtros e Ordenação
68
+ */
69
+ async list(options?: ListOptions<T>): Promise<T[]> {
70
+ const params: any = {};
71
+
72
+ // Converte os objetos do SDK para strings que o Backend entende
73
+ if (options?.filter) {
74
+ params.filter = JSON.stringify(options.filter);
75
+ }
76
+
77
+ if (options?.sort) {
78
+ // Backend espera formato array: ["campo", "DESC"]
79
+ params.sort = JSON.stringify([options.sort.field, options.sort.order]);
80
+ }
81
+
82
+ const { data } = await this.http.get(`/db/${this.collectionName}`, {
83
+ params,
84
+ });
85
+ return data.data;
52
86
  }
53
87
 
54
88
  /**
55
- * Lista todos os documentos da coleção.
56
- * (GET /v1/db/tasks)
89
+ * Busca um documento pelo ID.
57
90
  */
58
- async list() {
59
- const { data } = await this.http.get(`/db/${this.collectionName}`);
60
- return data.data; // Retorna o array de documentos
91
+ async get(id: string): Promise<T> {
92
+ const { data } = await this.http.get(`/db/${this.collectionName}/${id}`);
93
+ return data.data;
61
94
  }
62
95
 
63
96
  /**
64
- * Cria um novo documento na coleção.
65
- * (POST /v1/db/tasks)
66
- * @param newData O objeto de dados a ser criado
97
+ * Cria um novo documento.
98
+ * O Partial<T> permite criar sem passar campos gerados (como id, createdAt).
67
99
  */
68
- async create(newData: Record<string, any>) {
100
+ async create(newData: Partial<T>): Promise<T> {
69
101
  const { data } = await this.http.post(
70
102
  `/db/${this.collectionName}`,
71
103
  newData
72
104
  );
73
- return data.data; // Retorna o documento criado
105
+ return data.data;
74
106
  }
75
107
 
76
108
  /**
77
109
  * Atualiza um documento existente.
78
- * (PUT /v1/db/tasks/:id)
79
- * @param id O ID do documento
80
- * @param updates Os campos a serem atualizados
81
110
  */
82
- async update(id: string, updates: Record<string, any>) {
111
+ async update(id: string, updates: Partial<T>): Promise<T> {
83
112
  const { data } = await this.http.put(
84
113
  `/db/${this.collectionName}/${id}`,
85
114
  updates
86
115
  );
87
- return data.data; // Retorna o documento atualizado
116
+ return data.data;
88
117
  }
89
118
 
90
119
  /**
91
120
  * Deleta um documento.
92
- * (DELETE /v1/db/tasks/:id)
93
- * @param id O ID do documento
94
121
  */
95
- async delete(id: string) {
96
- const { data } = await this.http.delete(`/db/${this.collectionName}/${id}`);
97
- return data; // Retorna { ok: true, ... }
122
+ async delete(id: string): Promise<boolean> {
123
+ await this.http.delete(`/db/${this.collectionName}/${id}`);
124
+ return true;
98
125
  }
99
126
 
100
127
  /**
101
- * Inscreve-se para mudanças em tempo real na coleção.
102
- * @param callback A função que será chamada com (action, data)
103
- * @returns Uma função 'unsubscribe' para fechar a conexão
128
+ * Inscreve-se para mudanças em tempo real.
129
+ * O callback recebe os dados tipados como T.
104
130
  */
105
131
  subscribe(
106
- callback: (action: "create" | "update" | "delete", data: any) => void
132
+ callback: (action: "create" | "update" | "delete", data: T) => void
107
133
  ) {
108
134
  const token = this.client.getToken();
109
135
  const projectId = this.client.projectId;
110
136
 
111
137
  if (!token || !projectId) {
112
- throw new Error("Não é possível se inscrever sem um token e projectId.");
138
+ console.warn("[SDK] Realtime falhou: Token ou ProjectId ausentes.");
139
+ return () => {};
113
140
  }
114
141
 
115
- // Constrói a URL: ws://localhost:3000/v1/db/subscribe/tasks?token=...&projectId=...
116
142
  const url = `${this.wsUrl}/v1/db/subscribe/${this.collectionName}?token=${token}&projectId=${projectId}`;
117
143
 
118
- const ws = new WebSocket(url);
119
-
120
- ws.on("open", () => {
121
- console.log(
122
- `[SDK] Inscrito em tempo real na coleção '${this.collectionName}'`
123
- );
124
- });
125
-
126
- ws.on("message", (message: string) => {
127
- try {
128
- const payload = JSON.parse(message) as WebSocketMessage;
129
- // Chama o callback do usuário
130
- callback(payload.action, payload.data);
131
- } catch (e) {
132
- console.error("[SDK] Erro ao processar mensagem WS:", e);
133
- }
134
- });
135
-
136
- ws.on("close", () => {
137
- console.log(`[SDK] Desconectado da coleção '${this.collectionName}'`);
138
- });
139
-
140
- ws.on("error", (err) => {
141
- console.error("[SDK] Erro no WebSocket:", err.message);
142
- });
143
-
144
- // Retorna uma função que o usuário pode chamar para parar de "ouvir"
145
- return () => {
146
- ws.close();
147
- };
144
+ let ws: WebSocket | null = null;
145
+
146
+ try {
147
+ ws = new WebSocket(url);
148
+
149
+ ws.onopen = () => {
150
+ // Conectado
151
+ };
152
+
153
+ ws.onmessage = (event: any) => {
154
+ try {
155
+ const raw = event.data?.toString() || event.toString();
156
+ if (raw === "pong") return;
157
+
158
+ const payload = JSON.parse(raw) as WebSocketMessage<T>;
159
+ callback(payload.action, payload.data);
160
+ } catch (e) {
161
+ // Erro silencioso de parse
162
+ }
163
+ };
164
+
165
+ // Mantém a conexão viva (Heartbeat)
166
+ const pingInterval = setInterval(() => {
167
+ if (ws?.readyState === WebSocket.OPEN) ws.send("ping");
168
+ }, 30000);
169
+
170
+ return () => {
171
+ clearInterval(pingInterval);
172
+ if (ws) ws.close();
173
+ };
174
+ } catch (err) {
175
+ console.error("[SDK] Erro WS:", err);
176
+ return () => {};
177
+ }
148
178
  }
149
179
  }
@@ -0,0 +1,41 @@
1
+ // src/functions.ts
2
+ import type { AxiosInstance } from "axios";
3
+ import type { PlataformaClient } from "./index.js";
4
+
5
+ export class FunctionsModule {
6
+ private client: PlataformaClient;
7
+ private http: AxiosInstance;
8
+
9
+ constructor(client: PlataformaClient, http: AxiosInstance) {
10
+ this.client = client;
11
+ this.http = http;
12
+ }
13
+
14
+ /**
15
+ * Chama uma função HTTP Serverless.
16
+ * @param functionName O nome da função (ou rota, ex: 'pedidos/123')
17
+ * @param body (Opcional) Dados para enviar no corpo (POST)
18
+ * @param method (Opcional) Método HTTP (padrão POST se tiver body, GET se não)
19
+ */
20
+ async invoke<T = any>(
21
+ functionName: string,
22
+ body?: any,
23
+ method?: "GET" | "POST" | "PUT" | "DELETE"
24
+ ) {
25
+ const projectId = this.client.projectId;
26
+
27
+ // Remove barras iniciais para evitar URL malformada
28
+ const cleanName = functionName.replace(/^\//, "");
29
+
30
+ // Define método automaticamente se não informado
31
+ const finalMethod = method || (body ? "POST" : "GET");
32
+
33
+ const response = await this.http.request<T>({
34
+ url: `/functions/http/${projectId}/${cleanName}`,
35
+ method: finalMethod,
36
+ data: body,
37
+ });
38
+
39
+ return response.data;
40
+ }
41
+ }
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import { createHttpClient } from "./http-client.js";
4
4
  import { AuthModule } from "./auth.js";
5
5
  import { DatabaseModule } from "./database.js";
6
6
  import { StorageModule } from "./storage.js";
7
+ import { FunctionsModule } from "./functions.js"; // [NOVO] Import do módulo
7
8
 
8
9
  type ClientConfig = {
9
10
  apiUrl: string;
@@ -12,13 +13,14 @@ type ClientConfig = {
12
13
 
13
14
  /**
14
15
  * O cliente principal da Plataforma API.
15
- * Ponto de entrada para todos os módulos (Auth, DB, Storage).
16
+ * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions).
16
17
  */
17
18
  export class PlataformaClient {
18
19
  // Propriedades públicas (os "módulos")
19
20
  public auth: AuthModule;
20
21
  public db: DatabaseModule;
21
22
  public storage: StorageModule;
23
+ public functions: FunctionsModule; // [NOVO] Propriedade pública
22
24
 
23
25
  // Propriedades de configuração
24
26
  public apiUrl: string;
@@ -32,22 +34,24 @@ export class PlataformaClient {
32
34
  if (!config.apiUrl || !config.projectId) {
33
35
  throw new Error("apiUrl e projectId são obrigatórios.");
34
36
  }
35
- this.apiUrl = config.apiUrl.replace(/\/$/, ""); // Remove barra final
37
+ this.apiUrl = config.apiUrl.replace(/\/$/, ""); // Remove barra final se houver
36
38
  this.projectId = config.projectId;
37
39
 
38
- // Inicializa o cliente HTTP (passando 'this', a própria instância)
40
+ // Inicializa o cliente HTTP (passando 'this', a própria instância para injetar token/ID)
39
41
  this.http = createHttpClient(this);
40
42
 
41
- // Inicializa os módulos
43
+ // Inicializa os módulos passando a referência do cliente e do axios
42
44
  this.auth = new AuthModule(this, this.http);
43
45
  this.db = new DatabaseModule(this, this.http);
44
46
  this.storage = new StorageModule(this, this.http);
47
+ this.functions = new FunctionsModule(this, this.http); // [NOVO] Inicialização
45
48
  }
46
49
 
47
50
  // --- Gerenciamento de Token ---
48
51
 
49
52
  /**
50
53
  * Armazena o token de autenticação em memória.
54
+ * Chamado automaticamente pelo AuthModule após login.
51
55
  * @param token O JWT (ou null para logout)
52
56
  */
53
57
  setToken(token: string | null) {
@@ -56,6 +60,7 @@ export class PlataformaClient {
56
60
 
57
61
  /**
58
62
  * Recupera o token de autenticação atual.
63
+ * Usado pelo http-client para injetar o header Authorization.
59
64
  * @returns O JWT ou null
60
65
  */
61
66
  getToken(): string | null {
package/src/storage.ts CHANGED
@@ -1,12 +1,8 @@
1
1
  // src/storage.ts
2
2
  import type { AxiosInstance } from "axios";
3
3
  import type { PlataformaClient } from "./index.js";
4
- import axios from "axios"; // [A CORREÇÃO ESTÁ AQUI]
4
+ import axios from "axios";
5
5
 
6
- /**
7
- * Módulo de Storage
8
- * Lida com upload e download de arquivos.
9
- */
10
6
  export class StorageModule {
11
7
  private client: PlataformaClient;
12
8
  private http: AxiosInstance;
@@ -17,36 +13,68 @@ export class StorageModule {
17
13
  }
18
14
 
19
15
  /**
20
- * Faz o upload de um arquivo para o Storage do projeto.
21
- * Isso lida com o fluxo de "presign" (URL assinada) automaticamente.
22
- * @param file O objeto 'File' (do navegador) ou um Buffer (do Node.js)
23
- * @param fileName O nome do arquivo (ex: "imagem.png")
24
- * @param contentType O tipo (ex: "image/png")
25
- * @returns O objeto do arquivo criado no banco
16
+ * Faz o upload de um arquivo.
17
+ * @param file Arquivo (Browser: File, Node: Buffer)
18
+ * @param fileName Nome do arquivo (ex: 'foto.jpg')
19
+ * @param contentType MIME Type (ex: 'image/jpeg')
20
+ * @param folder (Opcional) Pasta de destino (ex: 'usuarios/123/')
26
21
  */
27
- async upload(file: File | Buffer, fileName: string, contentType: string) {
28
- const size = (file as File).size || (file as Buffer).length;
22
+ async upload(
23
+ file: File | Buffer | Blob,
24
+ fileName: string,
25
+ contentType: string,
26
+ folder?: string // [NOVO]
27
+ ) {
28
+ // Calcula tamanho de forma segura para Browser e Node
29
+ let size = 0;
30
+ if (typeof File !== "undefined" && file instanceof File) {
31
+ size = file.size;
32
+ } else if (typeof Blob !== "undefined" && file instanceof Blob) {
33
+ size = file.size;
34
+ } else if (file instanceof Buffer) {
35
+ // Node.js
36
+ size = file.length;
37
+ }
29
38
 
30
- // 1. Pedir a URL de upload para nossa API
39
+ // 1. Pedir URL assinada
31
40
  const { data: presignData } = await this.http.post("/storage/presign", {
32
- fileName: fileName,
33
- contentType: contentType,
34
- size: size,
41
+ fileName,
42
+ contentType,
43
+ size,
44
+ prefix: folder || "", // [NOVO] Envia o prefixo para o backend
35
45
  });
36
46
 
37
- // O 'presign' vem dentro de 'data'
38
47
  const presign = presignData.data;
39
- if (!presign?.url) {
40
- throw new Error("API não retornou uma URL de upload assinada.");
41
- }
48
+ if (!presign?.url) throw new Error("Falha ao obter URL de upload.");
42
49
 
43
- // 2. Enviar o arquivo diretamente para o Minio/S3 (sem auth)
44
- // Usamos o 'axios' global aqui, não o 'this.http' (que adicionaria auth)
50
+ // 2. Upload direto para S3
45
51
  await axios.put(presign.url, file, {
46
52
  headers: { "Content-Type": contentType },
47
53
  });
48
54
 
49
- // 3. Retorna os detalhes do objeto (downloadUrl, objectId, etc.)
50
- return presign;
55
+ return {
56
+ id: presign.objectId,
57
+ key: presign.key,
58
+ downloadUrl: presign.downloadUrl,
59
+ url: presign.downloadUrl, // Alias amigável
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Lista arquivos de uma pasta.
65
+ */
66
+ async list(folder = "") {
67
+ const { data } = await this.http.get("/storage/list", {
68
+ params: { projectId: this.client.projectId, prefix: folder },
69
+ });
70
+ return data.data; // Retorna { files: [], folders: [] }
71
+ }
72
+
73
+ /**
74
+ * Deleta um arquivo pelo ID.
75
+ */
76
+ async delete(fileId: string) {
77
+ await this.http.delete("/storage/delete", { data: { objectId: fileId } });
78
+ return true;
51
79
  }
52
80
  }