@aerostack/sdk-node 0.8.8 → 0.8.9

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.
Files changed (110) hide show
  1. package/package.json +9 -2
  2. package/FUNCTIONS.md +0 -95
  3. package/RUNTIMES.md +0 -48
  4. package/examples/aiAIChat.example.ts +0 -31
  5. package/examples/databaseDbQuery.example.ts +0 -34
  6. package/examples/e2e/__tests__/e2e.test.ts +0 -118
  7. package/examples/e2e/package.json +0 -15
  8. package/examples/e2e/vitest.config.ts +0 -8
  9. package/examples/express-integration.ts +0 -67
  10. package/examples/next-api-route.ts +0 -46
  11. package/examples/package.json +0 -18
  12. package/examples/standalone-auth.ts +0 -44
  13. package/jsr.json +0 -27
  14. package/src/__tests__/realtime.test.ts +0 -430
  15. package/src/__tests__/sdk.test.ts +0 -412
  16. package/src/_generated/apis/AIApi.ts +0 -477
  17. package/src/_generated/apis/AuthenticationApi.ts +0 -121
  18. package/src/_generated/apis/CacheApi.ts +0 -551
  19. package/src/_generated/apis/DatabaseApi.ts +0 -138
  20. package/src/_generated/apis/GatewayApi.ts +0 -204
  21. package/src/_generated/apis/QueueApi.ts +0 -218
  22. package/src/_generated/apis/ServicesApi.ts +0 -74
  23. package/src/_generated/apis/StorageApi.ts +0 -476
  24. package/src/_generated/apis/index.ts +0 -10
  25. package/src/_generated/index.ts +0 -5
  26. package/src/_generated/models/AuthResponse.ts +0 -88
  27. package/src/_generated/models/AuthSigninRequest.ts +0 -75
  28. package/src/_generated/models/AuthSignupRequest.ts +0 -91
  29. package/src/_generated/models/CacheDeleteMany200Response.ts +0 -81
  30. package/src/_generated/models/CacheDeleteManyRequest.ts +0 -66
  31. package/src/_generated/models/CacheExpireRequest.ts +0 -75
  32. package/src/_generated/models/CacheFlush200Response.ts +0 -73
  33. package/src/_generated/models/CacheFlushRequest.ts +0 -65
  34. package/src/_generated/models/CacheGet200Response.ts +0 -73
  35. package/src/_generated/models/CacheGetMany200Response.ts +0 -72
  36. package/src/_generated/models/CacheGetManyEntry.ts +0 -81
  37. package/src/_generated/models/CacheGetManyRequest.ts +0 -66
  38. package/src/_generated/models/CacheGetRequest.ts +0 -66
  39. package/src/_generated/models/CacheIncrement200Response.ts +0 -65
  40. package/src/_generated/models/CacheIncrementRequest.ts +0 -90
  41. package/src/_generated/models/CacheKeyEntry.ts +0 -73
  42. package/src/_generated/models/CacheKeys200Response.ts +0 -73
  43. package/src/_generated/models/CacheKeysRequest.ts +0 -65
  44. package/src/_generated/models/CacheListRequest.ts +0 -81
  45. package/src/_generated/models/CacheListResult.ts +0 -88
  46. package/src/_generated/models/CacheSet200Response.ts +0 -65
  47. package/src/_generated/models/CacheSetEntry.ts +0 -83
  48. package/src/_generated/models/CacheSetMany200Response.ts +0 -73
  49. package/src/_generated/models/CacheSetManyRequest.ts +0 -73
  50. package/src/_generated/models/CacheSetRequest.ts +0 -83
  51. package/src/_generated/models/ChatCompletionRequest.ts +0 -130
  52. package/src/_generated/models/ChatCompletionRequestStreamOptions.ts +0 -67
  53. package/src/_generated/models/ChatCompletionResponse.ts +0 -128
  54. package/src/_generated/models/ChatCompletionResponseChoicesInner.ts +0 -100
  55. package/src/_generated/models/ChatMessage.ts +0 -87
  56. package/src/_generated/models/ConfigureRequest.ts +0 -77
  57. package/src/_generated/models/DbBatchRequest.ts +0 -73
  58. package/src/_generated/models/DbBatchRequestQueriesInner.ts +0 -74
  59. package/src/_generated/models/DbBatchResult.ts +0 -80
  60. package/src/_generated/models/DbBatchResultResultsInner.ts +0 -81
  61. package/src/_generated/models/DbQueryRequest.ts +0 -74
  62. package/src/_generated/models/DbQueryResult.ts +0 -73
  63. package/src/_generated/models/DeleteByTypeRequest.ts +0 -66
  64. package/src/_generated/models/DeleteRequest.ts +0 -66
  65. package/src/_generated/models/ErrorResponse.ts +0 -99
  66. package/src/_generated/models/GatewayBillingLog200Response.ts +0 -73
  67. package/src/_generated/models/GatewayBillingLogRequest.ts +0 -92
  68. package/src/_generated/models/GatewayGetWallet200Response.ts +0 -72
  69. package/src/_generated/models/IngestRequest.ts +0 -91
  70. package/src/_generated/models/JobRecord.ts +0 -119
  71. package/src/_generated/models/ListTypes200Response.ts +0 -72
  72. package/src/_generated/models/Query200Response.ts +0 -72
  73. package/src/_generated/models/QueryRequest.ts +0 -90
  74. package/src/_generated/models/QueueCancelJob200Response.ts +0 -73
  75. package/src/_generated/models/QueueEnqueue201Response.ts +0 -73
  76. package/src/_generated/models/QueueEnqueueRequest.ts +0 -83
  77. package/src/_generated/models/QueueGetJob200Response.ts +0 -80
  78. package/src/_generated/models/QueueGetJobRequest.ts +0 -66
  79. package/src/_generated/models/QueueListJobs200Response.ts +0 -88
  80. package/src/_generated/models/QueueListJobsRequest.ts +0 -103
  81. package/src/_generated/models/SearchCount200Response.ts +0 -65
  82. package/src/_generated/models/SearchCountRequest.ts +0 -65
  83. package/src/_generated/models/SearchGet200Response.ts +0 -80
  84. package/src/_generated/models/SearchGetRequest.ts +0 -66
  85. package/src/_generated/models/SearchResult.ts +0 -97
  86. package/src/_generated/models/SearchUpdateRequest.ts +0 -91
  87. package/src/_generated/models/ServicesInvoke200Response.ts +0 -73
  88. package/src/_generated/models/ServicesInvokeRequest.ts +0 -75
  89. package/src/_generated/models/StorageCopy200Response.ts +0 -73
  90. package/src/_generated/models/StorageCopyRequest.ts +0 -75
  91. package/src/_generated/models/StorageExists200Response.ts +0 -65
  92. package/src/_generated/models/StorageGetRequest.ts +0 -66
  93. package/src/_generated/models/StorageListRequest.ts +0 -81
  94. package/src/_generated/models/StorageListResult.ts +0 -88
  95. package/src/_generated/models/StorageMetadata.ts +0 -97
  96. package/src/_generated/models/StorageMove200Response.ts +0 -73
  97. package/src/_generated/models/StorageMoveRequest.ts +0 -75
  98. package/src/_generated/models/StorageObject.ts +0 -97
  99. package/src/_generated/models/StorageUpload200Response.ts +0 -65
  100. package/src/_generated/models/TokenUsage.ts +0 -81
  101. package/src/_generated/models/TokenWallet.ts +0 -73
  102. package/src/_generated/models/TypeStats.ts +0 -73
  103. package/src/_generated/models/User.ts +0 -97
  104. package/src/_generated/models/index.ts +0 -80
  105. package/src/_generated/runtime.ts +0 -431
  106. package/src/index.ts +0 -3
  107. package/src/realtime.ts +0 -439
  108. package/src/sdk.ts +0 -317
  109. package/test_sdk.ts +0 -19
  110. package/tsconfig.json +0 -43
package/src/realtime.ts DELETED
@@ -1,439 +0,0 @@
1
- /**
2
- * Aerostack Realtime Client for Node.js SDK
3
- */
4
-
5
- export type RealtimeEvent = 'INSERT' | 'UPDATE' | 'DELETE' | '*' | string;
6
-
7
- export interface RealtimeMessage {
8
- type: string;
9
- topic: string;
10
- [key: string]: any;
11
- }
12
-
13
- export interface RealtimeSubscriptionOptions {
14
- event?: RealtimeEvent;
15
- filter?: Record<string, any>;
16
- }
17
-
18
- export type RealtimeCallback<T = any> = (payload: RealtimePayload<T>) => void;
19
-
20
- /** Typed payload for realtime events */
21
- export interface RealtimePayload<T = any> {
22
- type: 'db_change' | 'chat_message' | 'event';
23
- topic: string;
24
- operation?: RealtimeEvent;
25
- event?: string;
26
- data: T;
27
- old?: T;
28
- userId?: string;
29
- timestamp?: number | string;
30
- [key: string]: any;
31
- }
32
-
33
- /** Chat history message returned from REST API */
34
- export interface HistoryMessage {
35
- id: string;
36
- room_id: string;
37
- user_id: string;
38
- event: string;
39
- data: any;
40
- created_at: number;
41
- }
42
-
43
- export interface NodeRealtimeOptions {
44
- serverUrl: string;
45
- apiKey?: string;
46
- token?: string;
47
- projectId?: string;
48
- maxReconnectAttempts?: number;
49
- }
50
-
51
- const BASE_RECONNECT_MS = 1000;
52
- const MAX_RECONNECT_MS = 30000;
53
- const JITTER_FACTOR = 0.3;
54
-
55
- export class RealtimeSubscription<T = any> {
56
- private client: NodeRealtimeClient;
57
- topic: string;
58
- private options: RealtimeSubscriptionOptions;
59
- private callbacks: Map<string, Set<RealtimeCallback<T>>> = new Map();
60
- private _isSubscribed: boolean = false;
61
-
62
- constructor(client: NodeRealtimeClient, topic: string, options: RealtimeSubscriptionOptions = {}) {
63
- this.client = client;
64
- this.topic = topic;
65
- this.options = options;
66
- }
67
-
68
- /** Listen for DB change events (INSERT/UPDATE/DELETE/*) or custom named events */
69
- on(event: RealtimeEvent | string, callback: RealtimeCallback<T>): this {
70
- if (!this.callbacks.has(event)) {
71
- this.callbacks.set(event, new Set());
72
- }
73
- this.callbacks.get(event)!.add(callback);
74
- return this;
75
- }
76
-
77
- /** Remove a specific callback for an event */
78
- off(event: RealtimeEvent | string, callback: RealtimeCallback<T>): this {
79
- this.callbacks.get(event)?.delete(callback);
80
- return this;
81
- }
82
-
83
- subscribe(): this {
84
- if (this._isSubscribed) return this;
85
- this.client._send({
86
- type: 'subscribe',
87
- topic: this.topic,
88
- filter: this.options.filter
89
- });
90
- this._isSubscribed = true;
91
- return this;
92
- }
93
-
94
- unsubscribe(): void {
95
- if (!this._isSubscribed) return;
96
- this.client._send({
97
- type: 'unsubscribe',
98
- topic: this.topic
99
- });
100
- this._isSubscribed = false;
101
- this.callbacks.clear();
102
- this.client._removeSubscription(this.topic);
103
- }
104
-
105
- get isSubscribed() { return this._isSubscribed; }
106
-
107
- // ─── Phase 1: Pub/Sub — Publish custom events ─────────────────────────
108
- /** Publish a custom event to all subscribers on this channel */
109
- publish(event: string, data: any, options?: { persist?: boolean }): void {
110
- this.client._send({
111
- type: 'publish',
112
- topic: this.topic,
113
- event,
114
- data,
115
- persist: options?.persist,
116
- id: this.client._generateId(),
117
- });
118
- }
119
-
120
- // ─── Phase 2: Chat History ────────────────────────────────────────────
121
- /** Fetch persisted message history for this channel (requires persist: true on publish) */
122
- async getHistory(limit: number = 50, before?: number): Promise<HistoryMessage[]> {
123
- return this.client._fetchHistory(this.topic, limit, before);
124
- }
125
-
126
- // ─── Phase 3: Presence ────────────────────────────────────────────────
127
- /** Track this user's presence state on this channel (auto-synced to subscribers) */
128
- track(state: Record<string, any>): void {
129
- this.client._send({
130
- type: 'track',
131
- topic: this.topic,
132
- state,
133
- });
134
- }
135
-
136
- /** Stop tracking presence on this channel */
137
- untrack(): void {
138
- this.client._send({
139
- type: 'untrack',
140
- topic: this.topic,
141
- });
142
- }
143
-
144
- /** @internal */
145
- _emit(payload: RealtimePayload<T>): void {
146
- // DB change events (INSERT/UPDATE/DELETE)
147
- if (payload.operation) {
148
- const event = payload.operation as string;
149
- this.callbacks.get(event)?.forEach(cb => cb(payload));
150
- }
151
- // Custom named events ('player-moved', 'presence:join', etc.)
152
- if (payload.event) {
153
- this.callbacks.get(payload.event)?.forEach(cb => cb(payload));
154
- }
155
- // Catch-all
156
- this.callbacks.get('*')?.forEach(cb => {
157
- try { cb(payload); } catch (e) { console.error('Realtime callback error:', e); }
158
- });
159
- }
160
- }
161
-
162
- export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
163
-
164
- export class NodeRealtimeClient {
165
- private wsUrl: string;
166
- private apiKey?: string;
167
- private token?: string;
168
- private projectId?: string;
169
- private ws: WebSocket | null = null;
170
- private subscriptions: Map<string, RealtimeSubscription> = new Map();
171
- private reconnectTimer: any = null;
172
- private heartbeatTimer: any = null;
173
- private reconnectAttempts: number = 0;
174
- private _sendQueue: any[] = [];
175
- private _connectingPromise: Promise<void> | null = null;
176
- private _status: RealtimeStatus = 'idle';
177
- private _statusListeners: Set<(s: RealtimeStatus) => void> = new Set();
178
- // HTTP base URL for REST endpoints (history, etc.)
179
- private _httpBaseUrl: string;
180
- private _lastPong: number = 0;
181
- private _maxReconnectAttempts: number;
182
- private _maxRetriesListeners: Set<() => void> = new Set();
183
-
184
- constructor(options: NodeRealtimeOptions) {
185
- const uri = new URL(options.serverUrl);
186
- uri.protocol = uri.protocol === 'https:' ? 'wss:' : 'ws:';
187
- uri.pathname = uri.pathname.replace(/\/v1\/?$/, '') + '/api/realtime';
188
- this.wsUrl = uri.toString();
189
- this._httpBaseUrl = options.serverUrl.replace(/\/v1\/?$/, '');
190
- this.apiKey = options.apiKey;
191
- this.token = options.token;
192
- this.projectId = options.projectId;
193
- this._maxReconnectAttempts = options.maxReconnectAttempts ?? Infinity;
194
- }
195
-
196
- get status(): RealtimeStatus { return this._status; }
197
- get connected(): boolean { return this._status === 'connected'; }
198
-
199
- onStatusChange(cb: (status: RealtimeStatus) => void): () => void {
200
- this._statusListeners.add(cb);
201
- return () => this._statusListeners.delete(cb);
202
- }
203
-
204
- onMaxRetriesExceeded(cb: () => void): () => void {
205
- this._maxRetriesListeners.add(cb);
206
- return () => this._maxRetriesListeners.delete(cb);
207
- }
208
-
209
- private _setStatus(s: RealtimeStatus) {
210
- this._status = s;
211
- this._statusListeners.forEach(cb => cb(s));
212
- }
213
-
214
- setToken(newToken: string): void {
215
- this.token = newToken;
216
- this._send({ type: 'auth', token: newToken });
217
- }
218
-
219
- async connect(): Promise<void> {
220
- if (this.ws && this._status === 'connected') return;
221
- if (this._connectingPromise) return this._connectingPromise;
222
- this._connectingPromise = this._doConnect().finally(() => {
223
- this._connectingPromise = null;
224
- });
225
- return this._connectingPromise;
226
- }
227
-
228
- private async _doConnect(): Promise<void> {
229
- this._setStatus('connecting');
230
- const url = new URL(this.wsUrl);
231
- if (this.projectId) url.searchParams.set('projectId', this.projectId);
232
-
233
- return new Promise(async (resolve, reject) => {
234
- try {
235
- let WsClass: any;
236
- if (typeof globalThis.WebSocket !== 'undefined') {
237
- WsClass = globalThis.WebSocket;
238
- } else {
239
- try {
240
- const ws = await import('ws');
241
- WsClass = ws.default || ws;
242
- } catch {
243
- throw new Error('WebSocket not available. Install "ws" package.');
244
- }
245
- }
246
-
247
- // SECURITY: Pass credentials via Sec-WebSocket-Protocol header — never as URL query params
248
- // (URL params appear in CDN logs, browser history, and Referer headers).
249
- const protocols: string[] = [];
250
- if (this.apiKey) protocols.push(`aerostack-key.${this.apiKey}`);
251
- if (this.token) protocols.push(`aerostack-token.${this.token}`);
252
- if (protocols.length > 0) protocols.push('aerostack-v1');
253
- const protocolsArg = protocols.length > 0 ? protocols : undefined;
254
- this.ws = protocolsArg ? new WsClass(url.toString(), protocolsArg) : new WsClass(url.toString());
255
-
256
- this.ws!.onopen = () => {
257
- this._setStatus('connected');
258
- this.reconnectAttempts = 0;
259
- this._lastPong = Date.now();
260
- this.startHeartbeat();
261
- while (this._sendQueue.length > 0) {
262
- this.ws!.send(JSON.stringify(this._sendQueue.shift()));
263
- }
264
- for (const sub of this.subscriptions.values()) {
265
- if (sub.isSubscribed) sub.subscribe();
266
- }
267
- resolve();
268
- };
269
-
270
- this.ws!.onmessage = (event: any) => {
271
- try {
272
- const raw = typeof event === 'string' ? event : event.data;
273
- const data: RealtimeMessage = JSON.parse(raw);
274
- this.handleMessage(data);
275
- } catch (e) {
276
- console.error('Realtime message parse error:', e);
277
- }
278
- };
279
-
280
- this.ws!.onclose = () => {
281
- this._setStatus('reconnecting');
282
- this.stopHeartbeat();
283
- this.ws = null;
284
- this.scheduleReconnect();
285
- };
286
-
287
- this.ws!.onerror = (err: any) => {
288
- console.error('Realtime connection error:', err);
289
- this._setStatus('disconnected');
290
- reject(err);
291
- };
292
- } catch (e) {
293
- this._setStatus('disconnected');
294
- reject(e);
295
- }
296
- });
297
- }
298
-
299
- disconnect(): void {
300
- this._setStatus('disconnected');
301
- this.stopReconnect();
302
- this.stopHeartbeat();
303
- if (this.ws) {
304
- this.ws.close();
305
- this.ws = null;
306
- }
307
- this._sendQueue = [];
308
- }
309
-
310
- channel<T = any>(topic: string, options: RealtimeSubscriptionOptions = {}): RealtimeSubscription<T> {
311
- if (!this.projectId) {
312
- throw new Error('projectId is required for channel subscriptions. Set it in NodeRealtimeOptions.');
313
- }
314
- let fullTopic: string;
315
- if (!topic.includes('/')) {
316
- fullTopic = `table/${topic}/${this.projectId}`;
317
- } else if (this.projectId && topic.endsWith(`/${this.projectId}`)) {
318
- fullTopic = topic; // already fully qualified
319
- } else {
320
- fullTopic = `${topic}/${this.projectId}`; // e.g. 'table/orders' → 'table/orders/<projectId>'
321
- }
322
- let sub = this.subscriptions.get(fullTopic);
323
- if (!sub) {
324
- sub = new RealtimeSubscription<T>(this, fullTopic, options);
325
- this.subscriptions.set(fullTopic, sub);
326
- }
327
- return sub as RealtimeSubscription<T>;
328
- }
329
-
330
- /** Legacy: send a chat message (now persisted to DB) */
331
- sendChat(roomId: string, text: string, metadata?: Record<string, any>): void {
332
- this._send({ type: 'chat', roomId, text, metadata });
333
- }
334
-
335
- /** @internal — Generate unique message ID for ack tracking */
336
- _generateId(): string {
337
- return Math.random().toString(36).slice(2) + Date.now().toString(36);
338
- }
339
-
340
- /** @internal — Remove a subscription from the map (called on unsubscribe) */
341
- _removeSubscription(topic: string): void {
342
- this.subscriptions.delete(topic);
343
- }
344
-
345
- /** @internal */
346
- _send(data: any): void {
347
- if (this.ws && this._status === 'connected') {
348
- this.ws.send(JSON.stringify(data));
349
- } else {
350
- this._sendQueue.push(data);
351
- }
352
- }
353
-
354
- /** @internal — Fetch chat/event history via REST API */
355
- async _fetchHistory(room: string, limit: number = 50, before?: number): Promise<HistoryMessage[]> {
356
- const url = new URL(`${this._httpBaseUrl}/api/v1/public/realtime/history`);
357
- url.searchParams.set('room', room);
358
- url.searchParams.set('limit', String(limit));
359
- if (before) url.searchParams.set('before', String(before));
360
-
361
- const headers: Record<string, string> = {};
362
- if (this.apiKey) headers['X-Aerostack-Key'] = this.apiKey;
363
- if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
364
-
365
- const res = await fetch(url.toString(), { headers });
366
- const json = await res.json() as any;
367
- return json.messages || [];
368
- }
369
-
370
- private handleMessage(data: RealtimeMessage): void {
371
- // Track pong for liveness
372
- if (data.type === 'pong') {
373
- this._lastPong = Date.now();
374
- return;
375
- }
376
-
377
- // Ack (fire-and-forget acknowledgment from server)
378
- if (data.type === 'ack') {
379
- return;
380
- }
381
-
382
- // Route to subscription: db_change, chat_message, event, presence:*
383
- if (data.type === 'db_change' || data.type === 'chat_message' || data.type === 'event') {
384
- const sub = this.subscriptions.get(data.topic);
385
- if (sub) sub._emit(data as any);
386
- }
387
-
388
- // Re-key subscription on server-confirmed topic (for non-TS SDKs compatibility)
389
- if (data.type === 'subscribed' && data.topic) {
390
- for (const [origTopic, sub] of this.subscriptions.entries()) {
391
- if (data.topic !== origTopic && data.topic.startsWith(origTopic)) {
392
- this.subscriptions.delete(origTopic);
393
- sub.topic = data.topic;
394
- this.subscriptions.set(data.topic, sub);
395
- break;
396
- }
397
- }
398
- }
399
- }
400
-
401
- private startHeartbeat(): void {
402
- this.heartbeatTimer = setInterval(() => {
403
- this._send({ type: 'ping' });
404
- if (this._lastPong > 0 && Date.now() - this._lastPong > 70000) {
405
- console.warn('Realtime: no pong received, forcing reconnect');
406
- this.ws?.close();
407
- }
408
- }, 30000);
409
- }
410
-
411
- private stopHeartbeat(): void {
412
- if (this.heartbeatTimer) {
413
- clearInterval(this.heartbeatTimer);
414
- this.heartbeatTimer = null;
415
- }
416
- }
417
-
418
- private scheduleReconnect(): void {
419
- this.stopReconnect();
420
- if (this.reconnectAttempts >= this._maxReconnectAttempts) {
421
- this._setStatus('disconnected');
422
- this._maxRetriesListeners.forEach(cb => cb());
423
- return;
424
- }
425
- const delay = Math.min(BASE_RECONNECT_MS * Math.pow(2, this.reconnectAttempts), MAX_RECONNECT_MS);
426
- const jitter = delay * JITTER_FACTOR * Math.random();
427
- this.reconnectAttempts++;
428
- this.reconnectTimer = setTimeout(() => {
429
- this.connect().catch(() => { });
430
- }, delay + jitter);
431
- }
432
-
433
- private stopReconnect(): void {
434
- if (this.reconnectTimer) {
435
- clearTimeout(this.reconnectTimer);
436
- this.reconnectTimer = null;
437
- }
438
- }
439
- }