@zveltio/sdk 1.2.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.
Files changed (53) hide show
  1. package/README.md +191 -0
  2. package/dist/client/Auth.d.ts +19 -0
  3. package/dist/client/Auth.d.ts.map +1 -0
  4. package/dist/client/Auth.js +45 -0
  5. package/dist/client/QueryBuilder.d.ts +17 -0
  6. package/dist/client/QueryBuilder.d.ts.map +1 -0
  7. package/dist/client/QueryBuilder.js +76 -0
  8. package/dist/client/RealtimeClient.d.ts +22 -0
  9. package/dist/client/RealtimeClient.d.ts.map +1 -0
  10. package/dist/client/RealtimeClient.js +96 -0
  11. package/dist/client/ZveltioClient.d.ts +20 -0
  12. package/dist/client/ZveltioClient.d.ts.map +1 -0
  13. package/dist/client/ZveltioClient.js +78 -0
  14. package/dist/client.d.ts +82 -0
  15. package/dist/client.d.ts.map +1 -0
  16. package/dist/client.js +122 -0
  17. package/dist/core.d.ts +61 -0
  18. package/dist/core.d.ts.map +1 -0
  19. package/dist/core.js +65 -0
  20. package/dist/crdt.d.ts +41 -0
  21. package/dist/crdt.d.ts.map +1 -0
  22. package/dist/crdt.js +87 -0
  23. package/dist/extension/index.d.ts +46 -0
  24. package/dist/extension/index.d.ts.map +1 -0
  25. package/dist/extension/index.js +1 -0
  26. package/dist/index.d.ts +14 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +9 -0
  29. package/dist/local-store.d.ts +68 -0
  30. package/dist/local-store.d.ts.map +1 -0
  31. package/dist/local-store.js +263 -0
  32. package/dist/realtime.d.ts +14 -0
  33. package/dist/realtime.d.ts.map +1 -0
  34. package/dist/realtime.js +82 -0
  35. package/dist/schema-watcher.d.ts +41 -0
  36. package/dist/schema-watcher.d.ts.map +1 -0
  37. package/dist/schema-watcher.js +182 -0
  38. package/dist/svelte.d.ts +38 -0
  39. package/dist/svelte.d.ts.map +1 -0
  40. package/dist/svelte.js +48 -0
  41. package/dist/sync-manager.d.ts +73 -0
  42. package/dist/sync-manager.d.ts.map +1 -0
  43. package/dist/sync-manager.js +293 -0
  44. package/dist/tests/local-store.test.d.ts +2 -0
  45. package/dist/tests/local-store.test.d.ts.map +1 -0
  46. package/dist/tests/local-store.test.js +76 -0
  47. package/dist/tests/setup.d.ts +2 -0
  48. package/dist/tests/setup.d.ts.map +1 -0
  49. package/dist/tests/setup.js +3 -0
  50. package/dist/types/index.d.ts +39 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/index.js +1 -0
  53. package/package.json +35 -0
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # @zveltio/sdk
2
+
3
+ TypeScript SDK for Zveltio — HTTP client, Realtime WebSocket, and Local-First offline support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @zveltio/sdk
9
+ ```
10
+
11
+ ## Basic Usage
12
+
13
+ ```typescript
14
+ import { createZveltioClient } from '@zveltio/sdk';
15
+
16
+ const client = createZveltioClient({ baseUrl: 'https://api.myapp.com' });
17
+
18
+ // CRUD
19
+ const todos = await client.collection('todos').list();
20
+ await client.collection('todos').create({ title: 'Buy milk', done: false });
21
+ await client.collection('todos').update('id-123', { done: true });
22
+ await client.collection('todos').delete('id-123');
23
+ ```
24
+
25
+ ## Live TypeScript Types
26
+
27
+ Get full IntelliSense on every `.collection()` call by pointing the client at your generated schema.
28
+
29
+ **Step 1 — Generate types** (one-shot or watch mode):
30
+
31
+ ```bash
32
+ # One-shot
33
+ bunx zveltio generate-types --url http://localhost:3000 --out ./src/zveltio-types.d.ts
34
+
35
+ # Watch mode (re-generates on every schema change while developing)
36
+ bunx zveltio dev --watch
37
+ ```
38
+
39
+ **Step 2 — Import the generated alias:**
40
+
41
+ ```typescript
42
+ import type { ZveltioSchema } from './zveltio-types';
43
+ ```
44
+
45
+ **Step 3 — Pass it to `createZveltioClient`:**
46
+
47
+ ```typescript
48
+ import { createZveltioClient } from '@zveltio/sdk';
49
+ import type { ZveltioSchema } from './zveltio-types';
50
+
51
+ const client = createZveltioClient<ZveltioSchema>({
52
+ baseUrl: 'https://api.myapp.com',
53
+ });
54
+ ```
55
+
56
+ **Step 4 — Every `.collection()` call is now fully typed:**
57
+
58
+ ```typescript
59
+ // TypeScript knows the shape of each record automatically
60
+ const { data: products } = await client.collection('products').list();
61
+ // ^-- typed as Products[] (from your generated CollectionTypeMap)
62
+
63
+ const order = await client.collection('orders').getOne('ord-123');
64
+ // ^-- typed as Orders
65
+
66
+ await client.collection('products').create({ name: 'Widget', price: 9.99 });
67
+ // TypeScript will error if you pass unknown fields or the wrong types
68
+ ```
69
+
70
+ The generated file (`zveltio-types.d.ts`) exports individual interfaces per collection, a `CollectionTypeMap` interface, and the `ZveltioSchema` alias — all kept in sync automatically when you run in watch mode.
71
+
72
+ ## Realtime
73
+
74
+ ```typescript
75
+ import { ZveltioRealtime } from '@zveltio/sdk';
76
+
77
+ const rt = new ZveltioRealtime('https://api.myapp.com');
78
+ rt.connect();
79
+
80
+ const unsub = rt.subscribe('todos', (event) => {
81
+ console.log('Event:', event.event, event.record_id);
82
+ });
83
+
84
+ // Cleanup
85
+ unsub();
86
+ rt.disconnect();
87
+ ```
88
+
89
+ ## Local-First Usage
90
+
91
+ Zveltio SDK supports offline-first data access with automatic background sync:
92
+
93
+ ```typescript
94
+ import { createZveltioClient, SyncManager } from '@zveltio/sdk';
95
+
96
+ const client = createZveltioClient({ baseUrl: 'https://api.myapp.com' });
97
+ const sync = new SyncManager(client, {
98
+ syncInterval: 5000,
99
+ onConflict: (local, server) => {
100
+ // Custom merge: keep local 'notes' field, take rest from server
101
+ return { ...server, notes: local?.notes };
102
+ },
103
+ });
104
+
105
+ // Start: opens IndexedDB, connects WebSocket, starts periodic sync
106
+ await sync.start('https://api.myapp.com');
107
+
108
+ // All operations are instant (local-first)
109
+ const todos = sync.collection('todos');
110
+
111
+ // Create — writes locally, syncs in background
112
+ await todos.create({ title: 'Buy milk', done: false });
113
+
114
+ // List — reads from local IndexedDB (instant, works offline)
115
+ const all = await todos.list();
116
+ // each record has _syncStatus: 'synced' | 'pending' | 'conflict'
117
+
118
+ // Update — writes locally, syncs in background
119
+ await todos.update('id-123', { done: true });
120
+
121
+ // Delete — soft-deletes locally, syncs in background
122
+ await todos.delete('id-123');
123
+
124
+ // Subscribe — reactive updates (local writes + server push via WebSocket)
125
+ const unsub = todos.subscribe((records) => {
126
+ console.log('Todos updated:', records);
127
+ });
128
+
129
+ // Check sync status
130
+ const status = await sync.getStatus();
131
+ // { pending: 2, conflicts: 0, isOnline: true }
132
+
133
+ // Handle conflicts manually
134
+ const conflicts = await todos.getConflicts();
135
+ for (const conflict of conflicts) {
136
+ await todos.resolveConflict(conflict.id, { ...conflict.data, resolved: true });
137
+ }
138
+
139
+ // Cleanup
140
+ unsub();
141
+ await sync.stop();
142
+ ```
143
+
144
+ ## Svelte 5 Integration
145
+
146
+ ```svelte
147
+ <script lang="ts">
148
+ import { onDestroy } from 'svelte';
149
+ import { useSyncCollection, useSyncStatus } from '@zveltio/sdk';
150
+
151
+ let todos = $state<any[]>([]);
152
+ let syncStatus = $state({ pending: 0, conflicts: 0, isOnline: true });
153
+
154
+ const unsubTodos = useSyncCollection(sync, 'todos', (records) => {
155
+ todos = records;
156
+ });
157
+
158
+ const unsubStatus = useSyncStatus(sync, (s) => {
159
+ syncStatus = s;
160
+ });
161
+
162
+ onDestroy(() => {
163
+ unsubTodos();
164
+ unsubStatus();
165
+ });
166
+ </script>
167
+
168
+ {#if !syncStatus.isOnline}
169
+ <div class="alert alert-warning">Offline — changes will sync when reconnected</div>
170
+ {/if}
171
+
172
+ {#if syncStatus.pending > 0}
173
+ <div class="badge badge-warning">{syncStatus.pending} pending</div>
174
+ {/if}
175
+
176
+ {#each todos as todo}
177
+ <div class:opacity-60={todo._syncStatus === 'pending'}>
178
+ {todo.title}
179
+ {#if todo._syncStatus === 'conflict'}<span class="badge badge-error">conflict</span>{/if}
180
+ </div>
181
+ {/each}
182
+ ```
183
+
184
+ ## Engine Sync Endpoints
185
+
186
+ The engine exposes two endpoints for batch sync:
187
+
188
+ - `POST /api/sync/push` — send offline operations to server
189
+ - `POST /api/sync/pull` — pull changes since a timestamp
190
+
191
+ These are used automatically by `SyncManager`. You can also call them directly for custom sync logic.
@@ -0,0 +1,19 @@
1
+ import type { ZveltioConfig } from '../types/index.js';
2
+ export declare class Auth {
3
+ private config;
4
+ constructor(config: ZveltioConfig);
5
+ signIn(email: string, password: string): Promise<{
6
+ user: any;
7
+ session: any;
8
+ }>;
9
+ signUp(name: string, email: string, password: string): Promise<{
10
+ user: any;
11
+ }>;
12
+ signOut(): Promise<void>;
13
+ getSession(): Promise<{
14
+ user: any;
15
+ session: any;
16
+ } | null>;
17
+ signInWithOAuth(provider: 'google' | 'github' | 'microsoft'): void;
18
+ }
19
+ //# sourceMappingURL=Auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Auth.d.ts","sourceRoot":"","sources":["../../src/client/Auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,qBAAa,IAAI;IACf,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,aAAa;IAI3B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC;IAW7E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IAW7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,UAAU,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,OAAO,EAAE,GAAG,CAAA;KAAE,GAAG,IAAI,CAAC;IAQ/D,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,IAAI;CAGnE"}
@@ -0,0 +1,45 @@
1
+ export class Auth {
2
+ config;
3
+ constructor(config) {
4
+ this.config = config;
5
+ }
6
+ async signIn(email, password) {
7
+ const res = await fetch(`${this.config.baseUrl}/api/auth/sign-in/email`, {
8
+ method: 'POST',
9
+ credentials: 'include',
10
+ headers: { 'Content-Type': 'application/json' },
11
+ body: JSON.stringify({ email, password }),
12
+ });
13
+ if (!res.ok)
14
+ throw new Error((await res.json()).message || 'Sign in failed');
15
+ return res.json();
16
+ }
17
+ async signUp(name, email, password) {
18
+ const res = await fetch(`${this.config.baseUrl}/api/auth/sign-up/email`, {
19
+ method: 'POST',
20
+ credentials: 'include',
21
+ headers: { 'Content-Type': 'application/json' },
22
+ body: JSON.stringify({ name, email, password }),
23
+ });
24
+ if (!res.ok)
25
+ throw new Error((await res.json()).message || 'Sign up failed');
26
+ return res.json();
27
+ }
28
+ async signOut() {
29
+ await fetch(`${this.config.baseUrl}/api/auth/sign-out`, {
30
+ method: 'POST',
31
+ credentials: 'include',
32
+ });
33
+ }
34
+ async getSession() {
35
+ const res = await fetch(`${this.config.baseUrl}/api/me`, { credentials: 'include' });
36
+ if (!res.ok)
37
+ return null;
38
+ const data = await res.json();
39
+ return { user: data.user, session: {} };
40
+ }
41
+ // OAuth redirect
42
+ signInWithOAuth(provider) {
43
+ window.location.href = `${this.config.baseUrl}/api/auth/sign-in/${provider}`;
44
+ }
45
+ }
@@ -0,0 +1,17 @@
1
+ import type { ZveltioConfig, QueryOptions, QueryResponse } from '../types/index.js';
2
+ export declare class QueryBuilder {
3
+ private _collection;
4
+ private _config;
5
+ private _filters;
6
+ private _options;
7
+ constructor(collection: string, config: ZveltioConfig);
8
+ where(field: string, value: any): this;
9
+ where(field: string, op: string, value: any): this;
10
+ page(n: number): this;
11
+ limit(n: number): this;
12
+ sortBy(field: string, order?: 'asc' | 'desc'): this;
13
+ search(q: string): this;
14
+ query<T = any>(overrideOptions?: QueryOptions): Promise<QueryResponse<T>>;
15
+ [Symbol.asyncIterator]<T = any>(): AsyncGenerator<T[]>;
16
+ }
17
+ //# sourceMappingURL=QueryBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilder.d.ts","sourceRoot":"","sources":["../../src/client/QueryBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpF,qBAAa,YAAY;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,QAAQ,CAAoB;gBAExB,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;IAMrD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IACtC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAWlD,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IACrB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAGtB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,KAAK,GAAG,MAAc,GAAG,IAAI;IAO1D,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAGjB,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,eAAe,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAiCxE,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC;CAY9D"}
@@ -0,0 +1,76 @@
1
+ export class QueryBuilder {
2
+ _collection;
3
+ _config;
4
+ _filters = {};
5
+ _options = {};
6
+ constructor(collection, config) {
7
+ this._collection = collection;
8
+ this._config = config;
9
+ }
10
+ where(field, opOrValue, value) {
11
+ if (value === undefined) {
12
+ this._filters[field] = opOrValue;
13
+ }
14
+ else {
15
+ this._filters[field] = { [opOrValue]: value };
16
+ }
17
+ return this;
18
+ }
19
+ // Pagination
20
+ page(n) { this._options.page = n; return this; }
21
+ limit(n) { this._options.limit = n; return this; }
22
+ // Sorting
23
+ sortBy(field, order = 'asc') {
24
+ this._options.sort = field;
25
+ this._options.order = order;
26
+ return this;
27
+ }
28
+ // Search
29
+ search(q) { this._options.search = q; return this; }
30
+ // Execute
31
+ async query(overrideOptions) {
32
+ const opts = { ...this._options, ...overrideOptions };
33
+ const params = new URLSearchParams();
34
+ if (opts.page)
35
+ params.set('page', String(opts.page));
36
+ if (opts.limit)
37
+ params.set('limit', String(opts.limit));
38
+ if (opts.sort)
39
+ params.set('sort', opts.sort);
40
+ if (opts.order)
41
+ params.set('order', opts.order);
42
+ if (opts.search)
43
+ params.set('search', opts.search);
44
+ if (Object.keys(this._filters).length > 0) {
45
+ params.set('filter', JSON.stringify(this._filters));
46
+ }
47
+ const qs = params.toString();
48
+ const url = `/api/data/${this._collection}${qs ? '?' + qs : ''}`;
49
+ const headers = { 'Content-Type': 'application/json' };
50
+ if (this._config.apiKey)
51
+ headers['X-API-Key'] = this._config.apiKey;
52
+ const res = await fetch(`${this._config.baseUrl}${url}`, {
53
+ credentials: 'include',
54
+ headers,
55
+ });
56
+ if (!res.ok) {
57
+ const err = await res.json().catch(() => ({ error: res.statusText }));
58
+ throw new Error(err.error || `Query failed: ${res.status}`);
59
+ }
60
+ return res.json();
61
+ }
62
+ // Async iterable — allows `for await (const page of query)`
63
+ async *[Symbol.asyncIterator]() {
64
+ let currentPage = this._options.page || 1;
65
+ const pageSize = this._options.limit || 20;
66
+ while (true) {
67
+ const result = await this.page(currentPage).limit(pageSize).query();
68
+ if (result.records.length === 0)
69
+ break;
70
+ yield result.records;
71
+ if (currentPage >= result.pagination.pages)
72
+ break;
73
+ currentPage++;
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,22 @@
1
+ import type { RealtimeMessage } from '../types/index.js';
2
+ type EventCallback = (message: RealtimeMessage) => void;
3
+ type StatusCallback = (status: 'connecting' | 'connected' | 'disconnected') => void;
4
+ export declare class RealtimeClient {
5
+ private ws;
6
+ private baseUrl;
7
+ private subscriptions;
8
+ private statusCallbacks;
9
+ private reconnectDelay;
10
+ private maxReconnectDelay;
11
+ private shouldReconnect;
12
+ constructor(baseUrl: string);
13
+ connect(): void;
14
+ disconnect(): void;
15
+ subscribe(collection: string, event: string | '*', callback: EventCallback): () => void;
16
+ onStatusChange(callback: StatusCallback): () => void;
17
+ private sendSubscribe;
18
+ private sendUnsubscribe;
19
+ private notifyStatus;
20
+ }
21
+ export {};
22
+ //# sourceMappingURL=RealtimeClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealtimeClient.d.ts","sourceRoot":"","sources":["../../src/client/RealtimeClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;AACxD,KAAK,cAAc,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,KAAK,IAAI,CAAC;AAEpF,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAQ;gBAEnB,OAAO,EAAE,MAAM;IAI3B,OAAO,IAAI,IAAI;IA4Cf,UAAU,IAAI,IAAI;IAQlB,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI;IAwBvF,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI;IAKpD,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,YAAY;CAGrB"}
@@ -0,0 +1,96 @@
1
+ export class RealtimeClient {
2
+ ws = null;
3
+ baseUrl;
4
+ subscriptions = new Map();
5
+ statusCallbacks = new Set();
6
+ reconnectDelay = 1000;
7
+ maxReconnectDelay = 30000;
8
+ shouldReconnect = true;
9
+ constructor(baseUrl) {
10
+ this.baseUrl = baseUrl;
11
+ }
12
+ connect() {
13
+ if (this.ws?.readyState === WebSocket.OPEN)
14
+ return;
15
+ const wsUrl = this.baseUrl.replace(/^http/, 'ws') + '/api/ws';
16
+ this.ws = new WebSocket(wsUrl);
17
+ this.notifyStatus('connecting');
18
+ this.ws.onopen = () => {
19
+ this.reconnectDelay = 1000;
20
+ this.notifyStatus('connected');
21
+ // Re-subscribe all active subscriptions
22
+ for (const channel of this.subscriptions.keys()) {
23
+ this.sendSubscribe(channel);
24
+ }
25
+ };
26
+ this.ws.onmessage = (event) => {
27
+ try {
28
+ const message = JSON.parse(event.data);
29
+ const key = `${message.collection}:${message.event}`;
30
+ const wildcard = `${message.collection}:*`;
31
+ const allWildcard = `*:*`;
32
+ for (const [sub, callbacks] of this.subscriptions) {
33
+ if (sub === key || sub === wildcard || sub === allWildcard) {
34
+ callbacks.forEach((cb) => cb(message));
35
+ }
36
+ }
37
+ }
38
+ catch { /* ignore invalid messages */ }
39
+ };
40
+ this.ws.onclose = () => {
41
+ this.notifyStatus('disconnected');
42
+ if (this.shouldReconnect) {
43
+ setTimeout(() => this.connect(), this.reconnectDelay);
44
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
45
+ }
46
+ };
47
+ this.ws.onerror = () => {
48
+ this.ws?.close();
49
+ };
50
+ }
51
+ disconnect() {
52
+ this.shouldReconnect = false;
53
+ this.ws?.close();
54
+ this.ws = null;
55
+ }
56
+ // Subscribe to events on a collection
57
+ // pattern examples: 'products:insert', 'products:*', '*:*'
58
+ subscribe(collection, event, callback) {
59
+ const key = `${collection}:${event}`;
60
+ if (!this.subscriptions.has(key)) {
61
+ this.subscriptions.set(key, new Set());
62
+ }
63
+ this.subscriptions.get(key).add(callback);
64
+ if (this.ws?.readyState === WebSocket.OPEN) {
65
+ this.sendSubscribe(key);
66
+ }
67
+ else {
68
+ this.connect();
69
+ }
70
+ // Return unsubscribe function
71
+ return () => {
72
+ this.subscriptions.get(key)?.delete(callback);
73
+ if (this.subscriptions.get(key)?.size === 0) {
74
+ this.subscriptions.delete(key);
75
+ this.sendUnsubscribe(key);
76
+ }
77
+ };
78
+ }
79
+ onStatusChange(callback) {
80
+ this.statusCallbacks.add(callback);
81
+ return () => this.statusCallbacks.delete(callback);
82
+ }
83
+ sendSubscribe(channel) {
84
+ if (this.ws?.readyState === WebSocket.OPEN) {
85
+ this.ws.send(JSON.stringify({ type: 'subscribe', channel }));
86
+ }
87
+ }
88
+ sendUnsubscribe(channel) {
89
+ if (this.ws?.readyState === WebSocket.OPEN) {
90
+ this.ws.send(JSON.stringify({ type: 'unsubscribe', channel }));
91
+ }
92
+ }
93
+ notifyStatus(status) {
94
+ this.statusCallbacks.forEach((cb) => cb(status));
95
+ }
96
+ }
@@ -0,0 +1,20 @@
1
+ import type { ZveltioConfig, QueryOptions, QueryResponse, DeleteResponse } from '../types/index.js';
2
+ import { QueryBuilder } from './QueryBuilder.js';
3
+ import { Auth } from './Auth.js';
4
+ import { RealtimeClient } from './RealtimeClient.js';
5
+ export declare class ZveltioClient {
6
+ readonly auth: Auth;
7
+ readonly realtime: RealtimeClient;
8
+ private config;
9
+ constructor(config: ZveltioConfig);
10
+ from(collection: string): QueryBuilder;
11
+ list<T = any>(collection: string, options?: QueryOptions): Promise<QueryResponse<T>>;
12
+ get<T = any>(collection: string, id: string): Promise<T>;
13
+ create<T = any>(collection: string, data: Partial<T>): Promise<T>;
14
+ update<T = any>(collection: string, id: string, data: Partial<T>): Promise<T>;
15
+ replace<T = any>(collection: string, id: string, data: T): Promise<T>;
16
+ delete(collection: string, id: string): Promise<DeleteResponse>;
17
+ request<T>(path: string, init?: RequestInit): Promise<T>;
18
+ }
19
+ export declare function createClient(config: ZveltioConfig): ZveltioClient;
20
+ //# sourceMappingURL=ZveltioClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZveltioClient.d.ts","sourceRoot":"","sources":["../../src/client/ZveltioClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAkC,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpI,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,OAAO,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,aAAa;IAOjC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY;IAKhC,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAIpF,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAKxD,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQjE,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQ7E,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQrE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAO/D,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;CA4B/D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEjE"}
@@ -0,0 +1,78 @@
1
+ import { QueryBuilder } from './QueryBuilder.js';
2
+ import { Auth } from './Auth.js';
3
+ import { RealtimeClient } from './RealtimeClient.js';
4
+ export class ZveltioClient {
5
+ auth;
6
+ realtime;
7
+ config;
8
+ constructor(config) {
9
+ this.config = config;
10
+ this.auth = new Auth(config);
11
+ this.realtime = new RealtimeClient(config.baseUrl);
12
+ }
13
+ // Fluent query builder
14
+ from(collection) {
15
+ return new QueryBuilder(collection, this.config);
16
+ }
17
+ // Direct CRUD methods
18
+ async list(collection, options) {
19
+ return this.from(collection).query(options);
20
+ }
21
+ async get(collection, id) {
22
+ const res = await this.request(`/api/data/${collection}/${id}`);
23
+ return res.record;
24
+ }
25
+ async create(collection, data) {
26
+ const res = await this.request(`/api/data/${collection}`, {
27
+ method: 'POST',
28
+ body: JSON.stringify(data),
29
+ });
30
+ return res.record;
31
+ }
32
+ async update(collection, id, data) {
33
+ const res = await this.request(`/api/data/${collection}/${id}`, {
34
+ method: 'PATCH',
35
+ body: JSON.stringify(data),
36
+ });
37
+ return res.record;
38
+ }
39
+ async replace(collection, id, data) {
40
+ const res = await this.request(`/api/data/${collection}/${id}`, {
41
+ method: 'PUT',
42
+ body: JSON.stringify(data),
43
+ });
44
+ return res.record;
45
+ }
46
+ async delete(collection, id) {
47
+ return this.request(`/api/data/${collection}/${id}`, {
48
+ method: 'DELETE',
49
+ });
50
+ }
51
+ // Raw request helper
52
+ async request(path, init) {
53
+ const headers = {
54
+ 'Content-Type': 'application/json',
55
+ ...init?.headers,
56
+ };
57
+ if (this.config.apiKey) {
58
+ headers['X-API-Key'] = this.config.apiKey;
59
+ }
60
+ const res = await fetch(`${this.config.baseUrl}${path}`, {
61
+ ...init,
62
+ credentials: 'include',
63
+ headers,
64
+ });
65
+ if (res.status === 401) {
66
+ this.config.onUnauthorized?.();
67
+ throw new Error('Unauthorized');
68
+ }
69
+ if (!res.ok) {
70
+ const err = await res.json().catch(() => ({ error: res.statusText }));
71
+ throw new Error(err.error || `Request failed: ${res.status}`);
72
+ }
73
+ return res.json();
74
+ }
75
+ }
76
+ export function createClient(config) {
77
+ return new ZveltioClient(config);
78
+ }
@@ -0,0 +1,82 @@
1
+ export interface ZveltioClientConfig {
2
+ baseUrl: string;
3
+ apiKey?: string;
4
+ headers?: Record<string, string>;
5
+ }
6
+ export interface ListParams {
7
+ page?: number;
8
+ limit?: number;
9
+ sort?: string;
10
+ order?: 'asc' | 'desc';
11
+ search?: string;
12
+ filter?: Record<string, any>;
13
+ cursor?: string;
14
+ }
15
+ export interface ListResult<T> {
16
+ data: T[];
17
+ total: number;
18
+ page: number;
19
+ limit: number;
20
+ next_cursor?: string;
21
+ }
22
+ declare class CollectionRef<T extends Record<string, any>> {
23
+ private readonly name;
24
+ private readonly client;
25
+ constructor(name: string, client: ZveltioClient<any>);
26
+ list(params?: ListParams): Promise<ListResult<T>>;
27
+ getMany(params?: ListParams): Promise<ListResult<T>>;
28
+ get(id: string): Promise<T>;
29
+ getOne(id: string): Promise<T>;
30
+ create(data: Omit<T, 'id' | 'created_at' | 'updated_at'>): Promise<T>;
31
+ update(id: string, data: Partial<Omit<T, 'id' | 'created_at' | 'updated_at'>>): Promise<T>;
32
+ delete(id: string): Promise<{
33
+ success: boolean;
34
+ }>;
35
+ }
36
+ /**
37
+ * ZveltioClient — generic over your schema type.
38
+ *
39
+ * Usage with generated types (run `zveltio generate-types` once):
40
+ *
41
+ * import type { ZveltioSchema } from './zveltio-types';
42
+ * const client = createZveltioClient<ZveltioSchema>({ baseUrl: '...' });
43
+ * const { data } = await client.collection('products').list();
44
+ * // ^-- typed as your Products interface
45
+ *
46
+ * Usage without types (untyped, same as before):
47
+ * const client = createZveltioClient({ baseUrl: '...' });
48
+ */
49
+ export declare class ZveltioClient<Schema extends Record<string, any> = Record<string, any>> {
50
+ private baseUrl;
51
+ private headers;
52
+ constructor(config: ZveltioClientConfig);
53
+ private request;
54
+ get<T = any>(path: string): Promise<T>;
55
+ post<T = any>(path: string, body?: unknown): Promise<T>;
56
+ patch<T = any>(path: string, body?: unknown): Promise<T>;
57
+ delete<T = any>(path: string): Promise<T>;
58
+ upload<T = any>(path: string, formData: FormData): Promise<T>;
59
+ /**
60
+ * Returns a fully-typed collection reference.
61
+ * If Schema is provided (via createZveltioClient<MySchema>()),
62
+ * the return type reflects the collection's record shape.
63
+ */
64
+ collection<K extends keyof Schema & string>(name: K): CollectionRef<Schema[K]>;
65
+ collection(name: string): CollectionRef<Record<string, any>>;
66
+ /** Auth helpers */
67
+ readonly auth: {
68
+ readonly login: (email: string, password: string) => Promise<any>;
69
+ readonly signup: (email: string, password: string, name: string) => Promise<any>;
70
+ readonly logout: () => Promise<any>;
71
+ readonly session: () => Promise<any>;
72
+ };
73
+ /** Storage helpers */
74
+ readonly storage: {
75
+ readonly upload: (file: File, folder?: string) => Promise<any>;
76
+ readonly list: (folder?: string) => Promise<any>;
77
+ readonly delete: (key: string) => Promise<any>;
78
+ };
79
+ }
80
+ export declare function createZveltioClient<Schema extends Record<string, any> = Record<string, any>>(config: ZveltioClientConfig): ZveltioClient<Schema>;
81
+ export {};
82
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}