@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/database.js CHANGED
@@ -1,4 +1,5 @@
1
- import WebSocket from "ws";
1
+ // Ajuste para WebSocket funcionar no Browser (Vite) e Node
2
+ const WS = typeof window !== "undefined" ? window.WebSocket : global.WebSocket || null;
2
3
  /**
3
4
  * Builder para construção fluente de queries.
4
5
  */
@@ -7,79 +8,46 @@ export class QueryBuilder {
7
8
  this.filter = {};
8
9
  this.collectionRef = collectionRef;
9
10
  }
10
- /**
11
- * Adiciona um filtro de igualdade.
12
- */
13
11
  eq(column, value) {
14
12
  this.filter[column] = value;
15
13
  return this;
16
14
  }
17
- /**
18
- * Adiciona um filtro de desigualdade ($ne).
19
- */
20
15
  neq(column, value) {
21
16
  this.filter[column] = { ...this.filter[column], $ne: value };
22
17
  return this;
23
18
  }
24
- /**
25
- * Adiciona um filtro maior que ($gt).
26
- */
27
19
  gt(column, value) {
28
20
  this.filter[column] = { ...this.filter[column], $gt: value };
29
21
  return this;
30
22
  }
31
- /**
32
- * Adiciona um filtro maior ou igual ($gte).
33
- */
34
23
  gte(column, value) {
35
24
  this.filter[column] = { ...this.filter[column], $gte: value };
36
25
  return this;
37
26
  }
38
- /**
39
- * Adiciona um filtro menor que ($lt).
40
- */
41
27
  lt(column, value) {
42
28
  this.filter[column] = { ...this.filter[column], $lt: value };
43
29
  return this;
44
30
  }
45
- /**
46
- * Adiciona um filtro menor ou igual ($lte).
47
- */
48
31
  lte(column, value) {
49
32
  this.filter[column] = { ...this.filter[column], $lte: value };
50
33
  return this;
51
34
  }
52
- /**
53
- * Adiciona um filtro LIKE ($like).
54
- */
55
35
  like(column, value) {
56
36
  this.filter[column] = { ...this.filter[column], $like: value };
57
37
  return this;
58
38
  }
59
- /**
60
- * Define a ordenação.
61
- */
62
39
  order(column, direction = "ASC") {
63
40
  this.sort = { field: column, order: direction };
64
41
  return this;
65
42
  }
66
- /**
67
- * Define o limite de registros.
68
- */
69
43
  limit(count) {
70
44
  this.limitVal = count;
71
45
  return this;
72
46
  }
73
- /**
74
- * Define o deslocamento (paginação).
75
- */
76
47
  offset(count) {
77
48
  this.offsetVal = count;
78
49
  return this;
79
50
  }
80
- /**
81
- * Executa a query e retorna os resultados.
82
- */
83
51
  async get() {
84
52
  return this.collectionRef.list({
85
53
  filter: this.filter,
@@ -100,14 +68,63 @@ export class DatabaseModule {
100
68
  /**
101
69
  * Seleciona uma coleção de dados.
102
70
  * [PREMIUM] Suporta Generics <T> para tipagem forte.
103
- * * @example client.db.collection<Product>('products')
71
+ * @example client.db.collection<Product>('products')
104
72
  */
105
73
  collection(collectionName) {
106
74
  return new CollectionReference(this.client, this.http, collectionName);
107
75
  }
76
+ /**
77
+ * [NOVO] Método compatível com estilo 'Supabase/Drizzle'.
78
+ * É um alias para .collection() mas retorna interface compatível com o Showcase.
79
+ * @example client.db.from('posts').select('*')
80
+ */
81
+ from(tableName) {
82
+ const ref = this.collection(tableName);
83
+ return {
84
+ // Mapeia .select() para .list()
85
+ select: async (columns = "*") => {
86
+ try {
87
+ // Nota: O parametro 'columns' poderia ser enviado ao backend se suportado
88
+ const data = await ref.list();
89
+ return { data, error: null };
90
+ }
91
+ catch (err) {
92
+ return { data: null, error: err.response?.data || err.message };
93
+ }
94
+ },
95
+ // Mapeia .insert() para .create()
96
+ insert: async (data) => {
97
+ try {
98
+ const res = await ref.create(data);
99
+ return { data: res, error: null };
100
+ }
101
+ catch (err) {
102
+ return { data: null, error: err.response?.data || err.message };
103
+ }
104
+ },
105
+ // Mapeia .update() para .update() (Requer ID no payload ou logica extra)
106
+ update: async (data) => {
107
+ try {
108
+ if (!data.id)
109
+ throw new Error("ID é obrigatório para update via .from()");
110
+ const { id, ...updates } = data;
111
+ const res = await ref.update(id, updates);
112
+ return { data: res, error: null };
113
+ }
114
+ catch (err) {
115
+ return { data: null, error: err.response?.data || err.message };
116
+ }
117
+ },
118
+ // Mapeia .delete()
119
+ delete: async () => {
120
+ // O showcase não passou argumentos no delete, o que é perigoso.
121
+ // Aqui retornamos erro ou implementamos delete por query se suportado.
122
+ return { data: null, error: "Delete via .from() requer implementação de filtros" };
123
+ }
124
+ };
125
+ }
108
126
  /**
109
127
  * Executa múltiplas operações em uma única transação.
110
- * Se uma falhar, todas são revertidas.
111
128
  */
112
129
  async batch(operations) {
113
130
  const { data } = await this.http.post("/db/batch", { operations });
@@ -116,7 +133,6 @@ export class DatabaseModule {
116
133
  }
117
134
  /**
118
135
  * Referência a uma coleção específica.
119
- * O <T> define o formato dos dados (ex: interface User).
120
136
  */
121
137
  export class CollectionReference {
122
138
  constructor(client, http, name) {
@@ -127,93 +143,69 @@ export class CollectionReference {
127
143
  const protocol = client.apiUrl.startsWith("https") ? "wss" : "ws";
128
144
  this.wsUrl = client.apiUrl.replace(/^https?/, protocol);
129
145
  }
130
- /**
131
- * Inicia o QueryBuilder.
132
- * Atalho para .eq()
133
- */
146
+ // --- Atalhos de Query ---
134
147
  eq(column, value) {
135
148
  return new QueryBuilder(this).eq(column, value);
136
149
  }
137
- /**
138
- * Inicia o QueryBuilder.
139
- * Atalho para .gt()
140
- */
141
150
  gt(column, value) {
142
151
  return new QueryBuilder(this).gt(column, value);
143
152
  }
144
- // ... Outros atalhos podem ser adicionados conforme necessidade ...
145
- /**
146
- * Retorna uma nova instância do QueryBuilder.
147
- */
148
153
  query() {
149
154
  return new QueryBuilder(this);
150
155
  }
151
- /**
152
- * Lista documentos da coleção com filtros opcionais.
153
- * @param options Filtros e Ordenação
154
- */
156
+ // --- Operações CRUD ---
155
157
  async list(options) {
156
158
  const params = {};
157
- // Converte os objetos do SDK para strings que o Backend entende
158
159
  if (options?.filter) {
159
160
  params.filter = JSON.stringify(options.filter);
160
161
  }
161
162
  if (options?.sort) {
162
- // Backend espera formato array: ["campo", "DESC"]
163
163
  params.sort = JSON.stringify([options.sort.field, options.sort.order]);
164
164
  }
165
- // TODO: Backend precisa implementar limit/offset na rota GET
166
- // if (options?.limit) params.limit = options.limit;
167
- // if (options?.offset) params.offset = options.offset;
165
+ if (options?.limit)
166
+ params.limit = options.limit;
167
+ if (options?.offset)
168
+ params.offset = options.offset;
168
169
  const { data } = await this.http.get(`/db/${this.collectionName}`, {
169
170
  params,
170
171
  });
171
172
  return data.data;
172
173
  }
173
- /**
174
- * Busca um documento pelo ID.
175
- */
176
174
  async get(id) {
177
175
  const { data } = await this.http.get(`/db/${this.collectionName}/${id}`);
178
176
  return data.data;
179
177
  }
180
- /**
181
- * Cria um novo documento.
182
- * O Partial<T> permite criar sem passar campos gerados (como id, createdAt).
183
- */
184
178
  async create(newData) {
185
179
  const { data } = await this.http.post(`/db/${this.collectionName}`, newData);
186
180
  return data.data;
187
181
  }
188
- /**
189
- * Atualiza um documento existente.
190
- */
191
182
  async update(id, updates) {
192
183
  const { data } = await this.http.put(`/db/${this.collectionName}/${id}`, updates);
193
184
  return data.data;
194
185
  }
195
- /**
196
- * Deleta um documento.
197
- */
198
186
  async delete(id) {
199
187
  await this.http.delete(`/db/${this.collectionName}/${id}`);
200
188
  return true;
201
189
  }
202
- /**
203
- * Inscreve-se para mudanças em tempo real.
204
- * O callback recebe os dados já tipados como T.
205
- */
190
+ // --- Realtime ---
206
191
  subscribe(callback) {
192
+ if (!WS) {
193
+ console.warn("[SDK] WebSocket não disponível neste ambiente.");
194
+ return () => { };
195
+ }
207
196
  const token = this.client.getToken();
208
197
  const projectId = this.client.projectId;
209
198
  if (!token || !projectId) {
210
199
  console.warn("[SDK] Realtime falhou: Token ou ProjectId ausentes.");
211
200
  return () => { };
212
201
  }
202
+ // URL correta de subscribe
213
203
  const url = `${this.wsUrl}/v1/db/subscribe/${this.collectionName}?token=${token}&projectId=${projectId}`;
214
204
  let ws = null;
215
205
  try {
216
- ws = new WebSocket(url);
206
+ ws = new WS(url);
207
+ if (!ws)
208
+ return () => { };
217
209
  ws.onopen = () => {
218
210
  // Conectado
219
211
  };
@@ -229,10 +221,12 @@ export class CollectionReference {
229
221
  // Erro silencioso de parse
230
222
  }
231
223
  };
232
- // Mantém a conexão viva (Heartbeat)
224
+ // Heartbeat
233
225
  const pingInterval = setInterval(() => {
234
- if (ws?.readyState === WebSocket.OPEN)
226
+ // [CORREÇÃO] Adicionada verificação explicita 'ws &&' para evitar erro 'possibly null'
227
+ if (ws && ws.readyState === 1) { // 1 = OPEN
235
228
  ws.send("ping");
229
+ }
236
230
  }, 30000);
237
231
  return () => {
238
232
  clearInterval(pingInterval);
package/dist/index.d.ts CHANGED
@@ -8,36 +8,27 @@ import { PushModule } from "./push.js";
8
8
  * Configuração usada para criar o cliente principal da plataforma.
9
9
  */
10
10
  export type ClientConfig = {
11
- apiUrl: string;
12
- projectId: string;
11
+ apiUrl?: string;
12
+ baseUrl?: string;
13
+ projectId?: string;
14
+ apiKey?: string;
13
15
  };
14
- /**
15
- * O cliente principal da Plataforma API (Aether).
16
- * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
17
- */
18
16
  export declare class PlataformaClient {
19
17
  auth: AuthModule;
20
18
  db: DatabaseModule;
21
19
  storage: StorageModule;
22
20
  functions: FunctionsModule;
23
21
  push: PushModule;
22
+ database: DatabaseModule;
24
23
  apiUrl: string;
25
24
  projectId: string;
26
25
  http: AxiosInstance;
27
26
  private _token;
28
27
  constructor(config: ClientConfig);
29
- /**
30
- * Armazena o token de autenticação em memória.
31
- * Chamado automaticamente pelo AuthModule após login/registro.
32
- */
33
28
  setToken(token: string | null): void;
34
- /**
35
- * Recupera o token de autenticação atual.
36
- * Usado pelo http-client para injetar o header Authorization.
37
- */
38
29
  getToken(): string | null;
39
30
  }
40
31
  export { AetherError } from "./errors.js";
41
- export type { LoginResponse, Session } from "./auth.js";
32
+ export type { LoginResponse, Session, User } from "./auth.js";
42
33
  export type { ListOptions } from "./database.js";
43
34
  export type { PushPlatform, PushEnvironment, PushDevice, RegisterDeviceParams, SendPushResponse, PushStatus, PushLogEntry, ListPushLogsOptions, PushStats, } from "./push.js";
package/dist/index.js CHANGED
@@ -1,47 +1,36 @@
1
1
  import { createHttpClient } from "./http-client.js";
2
- import { AuthModule } from "./auth.js";
2
+ import { AuthModule } from "./auth.js"; // Importando User
3
3
  import { DatabaseModule } from "./database.js";
4
4
  import { StorageModule } from "./storage.js";
5
5
  import { FunctionsModule } from "./functions.js";
6
6
  import { PushModule } from "./push.js";
7
- /**
8
- * O cliente principal da Plataforma API (Aether).
9
- * Ponto de entrada para todos os módulos (Auth, DB, Storage, Functions, Push).
10
- */
11
7
  export class PlataformaClient {
12
8
  constructor(config) {
13
9
  this._token = null;
14
- if (!config.apiUrl || !config.projectId) {
15
- throw new Error("apiUrl e projectId são obrigatórios.");
10
+ // Resolve URL (prioridade para baseUrl se existir, senão apiUrl)
11
+ const url = config.baseUrl || config.apiUrl;
12
+ const project = config.apiKey || config.projectId;
13
+ if (!url || !project) {
14
+ throw new Error("baseUrl (API URL) e apiKey (Project ID) são obrigatórios.");
16
15
  }
17
- // Normaliza apiUrl removendo barras finais, se houver
18
- this.apiUrl = config.apiUrl.replace(/\/+$/, "");
19
- this.projectId = config.projectId;
20
- // Inicializa o cliente HTTP (passando a própria instância)
16
+ this.apiUrl = url.replace(/\/+$/, "");
17
+ this.projectId = project;
21
18
  this.http = createHttpClient(this);
22
- // Inicializa os módulos de alto nível
19
+ // Inicializa módulos
23
20
  this.auth = new AuthModule(this, this.http);
24
21
  this.db = new DatabaseModule(this, this.http);
25
22
  this.storage = new StorageModule(this, this.http);
26
23
  this.functions = new FunctionsModule(this, this.http);
27
24
  this.push = new PushModule(this, this.http);
25
+ // Cria o alias que o Showcase App espera
26
+ this.database = this.db;
28
27
  }
29
- // --- Gerenciamento de Token ---
30
- /**
31
- * Armazena o token de autenticação em memória.
32
- * Chamado automaticamente pelo AuthModule após login/registro.
33
- */
34
28
  setToken(token) {
35
29
  this._token = token;
36
30
  }
37
- /**
38
- * Recupera o token de autenticação atual.
39
- * Usado pelo http-client para injetar o header Authorization.
40
- */
41
31
  getToken() {
42
32
  return this._token;
43
33
  }
44
34
  }
45
- // Re-exports convenientes para quem consome o SDK
46
- // ===== ERRORS =====
35
+ // ===== EXPORTS =====
47
36
  export { AetherError } from "./errors.js";
package/dist/storage.d.ts CHANGED
@@ -4,25 +4,36 @@ export declare class StorageModule {
4
4
  private client;
5
5
  private http;
6
6
  constructor(client: PlataformaClient, http: AxiosInstance);
7
+ /**
8
+ * Seleciona um Bucket para operar.
9
+ * Ex: storage.from('avatars').upload(...)
10
+ */
11
+ from(bucketId: string): {
12
+ upload: (path: string, file: File | Blob | Buffer) => Promise<{
13
+ data: {
14
+ id: any;
15
+ key: any;
16
+ path: any;
17
+ downloadUrl: any;
18
+ url: any;
19
+ };
20
+ error: null;
21
+ }>;
22
+ list: () => Promise<any>;
23
+ };
7
24
  /**
8
25
  * 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/')
13
26
  */
14
27
  upload(file: File | Buffer | Blob, fileName: string, contentType: string, folder?: string): Promise<{
15
- id: any;
16
- key: any;
17
- downloadUrl: any;
18
- url: any;
28
+ data: {
29
+ id: any;
30
+ key: any;
31
+ path: any;
32
+ downloadUrl: any;
33
+ url: any;
34
+ };
35
+ error: null;
19
36
  }>;
20
- /**
21
- * Lista arquivos de uma pasta.
22
- */
23
37
  list(folder?: string): Promise<any>;
24
- /**
25
- * Deleta um arquivo pelo ID.
26
- */
27
38
  delete(fileId: string): Promise<boolean>;
28
39
  }
package/dist/storage.js CHANGED
@@ -4,15 +4,40 @@ export class StorageModule {
4
4
  this.client = client;
5
5
  this.http = http;
6
6
  }
7
+ // =================================================================
8
+ // COMPATIBILIDADE: MÈTODO .from() (Estilo Supabase/Drizzle)
9
+ // =================================================================
10
+ /**
11
+ * Seleciona um Bucket para operar.
12
+ * Ex: storage.from('avatars').upload(...)
13
+ */
14
+ from(bucketId) {
15
+ return {
16
+ upload: async (path, file) => {
17
+ // Tenta inferir o contentType se for File/Blob
18
+ let contentType = "application/octet-stream";
19
+ if (typeof File !== "undefined" && file instanceof File) {
20
+ contentType = file.type;
21
+ }
22
+ else if (typeof Blob !== "undefined" && file instanceof Blob) {
23
+ contentType = file.type;
24
+ }
25
+ // Chama o método upload original passando o bucket como folder/prefix
26
+ // O path vira o nome do arquivo final
27
+ return this.upload(file, path, contentType, bucketId);
28
+ },
29
+ list: async () => {
30
+ return this.list(bucketId);
31
+ }
32
+ };
33
+ }
34
+ // =================================================================
35
+ // MÉTODOS ORIGINAIS
36
+ // =================================================================
7
37
  /**
8
38
  * 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/')
13
39
  */
14
- async upload(file, fileName, contentType, folder // [NOVO]
15
- ) {
40
+ async upload(file, fileName, contentType, folder) {
16
41
  // Calcula tamanho de forma segura para Browser e Node
17
42
  let size = 0;
18
43
  if (typeof File !== "undefined" && file instanceof File) {
@@ -22,7 +47,6 @@ export class StorageModule {
22
47
  size = file.size;
23
48
  }
24
49
  else if (file instanceof Buffer) {
25
- // Node.js
26
50
  size = file.length;
27
51
  }
28
52
  // 1. Pedir URL assinada
@@ -30,34 +54,32 @@ export class StorageModule {
30
54
  fileName,
31
55
  contentType,
32
56
  size,
33
- prefix: folder || "", // [NOVO] Envia o prefixo para o backend
57
+ prefix: folder || "",
34
58
  });
35
59
  const presign = presignData.data;
36
60
  if (!presign?.url)
37
61
  throw new Error("Falha ao obter URL de upload.");
38
- // 2. Upload direto para S3
62
+ // 2. Upload direto para S3 (PUT na URL assinada)
39
63
  await axios.put(presign.url, file, {
40
64
  headers: { "Content-Type": contentType },
41
65
  });
42
66
  return {
43
- id: presign.objectId,
44
- key: presign.key,
45
- downloadUrl: presign.downloadUrl,
46
- url: presign.downloadUrl, // Alias amigável
67
+ data: {
68
+ id: presign.objectId,
69
+ key: presign.key,
70
+ path: presign.key, // Alias para facilitar o frontend
71
+ downloadUrl: presign.downloadUrl,
72
+ url: presign.downloadUrl,
73
+ },
74
+ error: null
47
75
  };
48
76
  }
49
- /**
50
- * Lista arquivos de uma pasta.
51
- */
52
77
  async list(folder = "") {
53
78
  const { data } = await this.http.get("/storage/list", {
54
79
  params: { projectId: this.client.projectId, prefix: folder },
55
80
  });
56
- return data.data; // Retorna { files: [], folders: [] }
81
+ return data.data;
57
82
  }
58
- /**
59
- * Deleta um arquivo pelo ID.
60
- */
61
83
  async delete(fileId) {
62
84
  await this.http.delete("/storage/delete", { data: { objectId: fileId } });
63
85
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allanfsouza/aether-sdk",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
4
4
  "description": "SDK do Cliente para a Plataforma Aether",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -32,4 +32,4 @@
32
32
  "@types/ws": "^8.5.10",
33
33
  "typescript": "^5.3.0"
34
34
  }
35
- }
35
+ }