@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/dist/client.js ADDED
@@ -0,0 +1,122 @@
1
+ class CollectionRef {
2
+ name;
3
+ client;
4
+ constructor(name, client) {
5
+ this.name = name;
6
+ this.client = client;
7
+ }
8
+ list(params) {
9
+ const qs = new URLSearchParams();
10
+ if (params?.page)
11
+ qs.set('page', String(params.page));
12
+ if (params?.limit)
13
+ qs.set('limit', String(params.limit));
14
+ if (params?.sort)
15
+ qs.set('sort', params.sort);
16
+ if (params?.order)
17
+ qs.set('order', params.order);
18
+ if (params?.search)
19
+ qs.set('search', params.search);
20
+ if (params?.filter)
21
+ qs.set('filter', JSON.stringify(params.filter));
22
+ if (params?.cursor)
23
+ qs.set('cursor', params.cursor);
24
+ const q = qs.toString();
25
+ return this.client['request']('GET', `/api/data/${this.name}${q ? `?${q}` : ''}`);
26
+ }
27
+ getMany(params) {
28
+ return this.list(params);
29
+ }
30
+ get(id) {
31
+ return this.client['request']('GET', `/api/data/${this.name}/${id}`);
32
+ }
33
+ getOne(id) {
34
+ return this.get(id);
35
+ }
36
+ create(data) {
37
+ return this.client['request']('POST', `/api/data/${this.name}`, data);
38
+ }
39
+ update(id, data) {
40
+ return this.client['request']('PATCH', `/api/data/${this.name}/${id}`, data);
41
+ }
42
+ delete(id) {
43
+ return this.client['request']('DELETE', `/api/data/${this.name}/${id}`);
44
+ }
45
+ }
46
+ /**
47
+ * ZveltioClient — generic over your schema type.
48
+ *
49
+ * Usage with generated types (run `zveltio generate-types` once):
50
+ *
51
+ * import type { ZveltioSchema } from './zveltio-types';
52
+ * const client = createZveltioClient<ZveltioSchema>({ baseUrl: '...' });
53
+ * const { data } = await client.collection('products').list();
54
+ * // ^-- typed as your Products interface
55
+ *
56
+ * Usage without types (untyped, same as before):
57
+ * const client = createZveltioClient({ baseUrl: '...' });
58
+ */
59
+ export class ZveltioClient {
60
+ baseUrl;
61
+ headers;
62
+ constructor(config) {
63
+ this.baseUrl = config.baseUrl.replace(/\/$/, '');
64
+ this.headers = {
65
+ 'Content-Type': 'application/json',
66
+ ...(config.apiKey ? { 'X-API-Key': config.apiKey } : {}),
67
+ ...config.headers,
68
+ };
69
+ }
70
+ async request(method, path, body) {
71
+ const res = await fetch(`${this.baseUrl}${path}`, {
72
+ method,
73
+ headers: this.headers,
74
+ credentials: 'include',
75
+ body: body ? JSON.stringify(body) : undefined,
76
+ });
77
+ if (!res.ok) {
78
+ const text = await res.text().catch(() => '');
79
+ throw new Error(`${method} ${path} failed: ${res.status} ${text}`);
80
+ }
81
+ return res.json();
82
+ }
83
+ get(path) { return this.request('GET', path); }
84
+ post(path, body) { return this.request('POST', path, body); }
85
+ patch(path, body) { return this.request('PATCH', path, body); }
86
+ delete(path) { return this.request('DELETE', path); }
87
+ async upload(path, formData) {
88
+ const headers = { ...this.headers };
89
+ delete headers['Content-Type'];
90
+ const res = await fetch(`${this.baseUrl}${path}`, {
91
+ method: 'POST', headers, credentials: 'include', body: formData,
92
+ });
93
+ if (!res.ok)
94
+ throw new Error(`Upload ${path} failed: ${res.status}`);
95
+ return res.json();
96
+ }
97
+ collection(name) {
98
+ return new CollectionRef(name, this);
99
+ }
100
+ /** Auth helpers */
101
+ auth = {
102
+ login: (email, password) => this.request('POST', '/api/auth/sign-in/email', { email, password }),
103
+ signup: (email, password, name) => this.request('POST', '/api/auth/sign-up/email', { email, password, name }),
104
+ logout: () => this.request('POST', '/api/auth/sign-out'),
105
+ session: () => this.request('GET', '/api/auth/get-session'),
106
+ };
107
+ /** Storage helpers */
108
+ storage = {
109
+ upload: (file, folder) => {
110
+ const fd = new FormData();
111
+ fd.append('file', file);
112
+ if (folder)
113
+ fd.append('folder', folder);
114
+ return this.upload('/api/storage/upload', fd);
115
+ },
116
+ list: (folder) => this.get(`/api/storage${folder ? `?folder=${folder}` : ''}`),
117
+ delete: (key) => this.delete(`/api/storage/${encodeURIComponent(key)}`),
118
+ };
119
+ }
120
+ export function createZveltioClient(config) {
121
+ return new ZveltioClient(config);
122
+ }
package/dist/core.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Framework-agnostic core logic shared by sdk-react and sdk-vue.
3
+ *
4
+ * Each function returns a plain Promise. Framework wrappers (React hooks /
5
+ * Vue composables) are responsible only for binding these to their reactive
6
+ * state primitives (useState/ref) and lifecycle hooks (useEffect/onMounted).
7
+ */
8
+ import type { ZveltioClient } from './client.js';
9
+ import type { ZveltioRealtime } from './realtime.js';
10
+ export interface CollectionOptions {
11
+ page?: number;
12
+ limit?: number;
13
+ sort?: string;
14
+ order?: 'asc' | 'desc';
15
+ filter?: Record<string, any>;
16
+ search?: string;
17
+ }
18
+ export interface CollectionResult<T> {
19
+ records: T[];
20
+ pagination: {
21
+ total: number;
22
+ page: number;
23
+ limit: number;
24
+ pages: number;
25
+ };
26
+ }
27
+ export interface AuthState {
28
+ user: any | null;
29
+ session: any | null;
30
+ }
31
+ export interface StorageFile {
32
+ id: string;
33
+ filename: string;
34
+ url: string;
35
+ size: number;
36
+ mime_type: string;
37
+ [key: string]: any;
38
+ }
39
+ export interface SyncStatus {
40
+ pendingOps: number;
41
+ isSyncing: boolean;
42
+ lastSyncAt: Date | null;
43
+ isOnline: boolean;
44
+ }
45
+ export declare function fetchCollection<T = any>(client: ZveltioClient, collectionName: string, options?: CollectionOptions): Promise<T[]>;
46
+ export declare function fetchRecord<T = any>(client: ZveltioClient, collectionName: string, id: string): Promise<T | null>;
47
+ export declare function createRecord<T = any>(client: ZveltioClient, collectionName: string, data: Partial<T>): Promise<T>;
48
+ export declare function updateRecord<T = any>(client: ZveltioClient, collectionName: string, id: string, data: Partial<T>): Promise<T>;
49
+ export declare function deleteRecord(client: ZveltioClient, collectionName: string, id: string): Promise<void>;
50
+ export declare function fetchSession(client: ZveltioClient): Promise<AuthState>;
51
+ export declare function loginUser(client: ZveltioClient, email: string, password: string): Promise<AuthState>;
52
+ export declare function logoutUser(client: ZveltioClient): Promise<AuthState>;
53
+ export declare function signupUser(client: ZveltioClient, email: string, password: string, name: string): Promise<AuthState>;
54
+ export declare function uploadFile(client: ZveltioClient, file: File, folder?: string): Promise<StorageFile>;
55
+ export declare function listFiles(client: ZveltioClient, folder?: string): Promise<StorageFile[]>;
56
+ export declare function removeFile(client: ZveltioClient, fileId: string): Promise<void>;
57
+ export declare function subscribeToCollection(realtime: ZveltioRealtime, collection: string, callback: (event: {
58
+ type: string;
59
+ data: any;
60
+ }) => void): () => void;
61
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
package/dist/core.js ADDED
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Framework-agnostic core logic shared by sdk-react and sdk-vue.
3
+ *
4
+ * Each function returns a plain Promise. Framework wrappers (React hooks /
5
+ * Vue composables) are responsible only for binding these to their reactive
6
+ * state primitives (useState/ref) and lifecycle hooks (useEffect/onMounted).
7
+ */
8
+ // ── Collection ────────────────────────────────────────────────────────────────
9
+ export async function fetchCollection(client, collectionName, options) {
10
+ const result = await client.collection(collectionName).list(options);
11
+ return result?.data ?? result?.records ?? result ?? [];
12
+ }
13
+ export async function fetchRecord(client, collectionName, id) {
14
+ const result = await client.collection(collectionName).get(id);
15
+ return result?.data ?? result?.record ?? result ?? null;
16
+ }
17
+ export async function createRecord(client, collectionName, data) {
18
+ const result = await client.collection(collectionName).create(data);
19
+ return result?.data ?? result?.record ?? result;
20
+ }
21
+ export async function updateRecord(client, collectionName, id, data) {
22
+ const result = await client.collection(collectionName).update(id, data);
23
+ return result?.data ?? result?.record ?? result;
24
+ }
25
+ export async function deleteRecord(client, collectionName, id) {
26
+ await client.collection(collectionName).delete(id);
27
+ }
28
+ // ── Auth ──────────────────────────────────────────────────────────────────────
29
+ export async function fetchSession(client) {
30
+ try {
31
+ const session = await client.auth.session();
32
+ return { user: session?.user ?? null, session };
33
+ }
34
+ catch {
35
+ return { user: null, session: null };
36
+ }
37
+ }
38
+ export async function loginUser(client, email, password) {
39
+ await client.auth.login(email, password);
40
+ return fetchSession(client);
41
+ }
42
+ export async function logoutUser(client) {
43
+ await client.auth.logout();
44
+ return { user: null, session: null };
45
+ }
46
+ export async function signupUser(client, email, password, name) {
47
+ await client.auth.signup(email, password, name);
48
+ return fetchSession(client);
49
+ }
50
+ // ── Storage ───────────────────────────────────────────────────────────────────
51
+ export async function uploadFile(client, file, folder) {
52
+ const result = await client.storage.upload(file, folder);
53
+ return result?.data ?? result?.file ?? result;
54
+ }
55
+ export async function listFiles(client, folder) {
56
+ const result = await client.storage.list(folder);
57
+ return result?.data ?? result?.files ?? result ?? [];
58
+ }
59
+ export async function removeFile(client, fileId) {
60
+ await client.storage.delete(fileId);
61
+ }
62
+ // ── Realtime ──────────────────────────────────────────────────────────────────
63
+ export function subscribeToCollection(realtime, collection, callback) {
64
+ return realtime.subscribe(collection, callback);
65
+ }
package/dist/crdt.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lightweight CRDT primitives for offline-first sync.
3
+ * No external dependencies — pure TypeScript.
4
+ */
5
+ export interface LWWField<T = any> {
6
+ value: T;
7
+ lamport: number;
8
+ clientId: string;
9
+ }
10
+ export type LWWDocument = Record<string, LWWField>;
11
+ /**
12
+ * Merge two LWW documents — field-level last-write-wins.
13
+ * The field with the higher lamport wins; on ties, higher clientId wins.
14
+ */
15
+ export declare function mergeLWW(local: LWWDocument, remote: LWWDocument): LWWDocument;
16
+ /** Convert a plain record to an LWW document */
17
+ export declare function toDocument(data: Record<string, any>, lamport: number, clientId: string): LWWDocument;
18
+ /** Extract plain data from an LWW document */
19
+ export declare function fromDocument(doc: LWWDocument): Record<string, any>;
20
+ export interface ORSetElement<T = any> {
21
+ value: T;
22
+ uid: string;
23
+ removed: boolean;
24
+ }
25
+ export type ORSet<T = any> = ORSetElement<T>[];
26
+ export declare function orSetAdd<T>(set: ORSet<T>, value: T): ORSet<T>;
27
+ export declare function orSetRemove<T>(set: ORSet<T>, uid: string): ORSet<T>;
28
+ export declare function orSetMerge<T>(local: ORSet<T>, remote: ORSet<T>): ORSet<T>;
29
+ export declare function orSetValues<T>(set: ORSet<T>): T[];
30
+ export declare class LamportClock {
31
+ private value;
32
+ private readonly clientId;
33
+ constructor(clientId: string, initial?: number);
34
+ /** Increment and return the new value */
35
+ tick(): number;
36
+ /** Update from a received timestamp, then increment */
37
+ update(received: number): number;
38
+ get current(): number;
39
+ get id(): string;
40
+ }
41
+ //# sourceMappingURL=crdt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crdt.d.ts","sourceRoot":"","sources":["../src/crdt.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,GAAG;IAC/B,KAAK,EAAE,CAAC,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,WAAW,CAkB7E;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,WAAW,CAMb;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAMlE;AAID,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,GAAG;IACnC,KAAK,EAAE,CAAC,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;AAE/C,wBAAgB,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAE7D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAEnE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAUzE;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAEjD;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,EAAE,MAAM,EAAE,OAAO,SAAI;IAKzC,yCAAyC;IACzC,IAAI,IAAI,MAAM;IAId,uDAAuD;IACvD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKhC,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,EAAE,IAAI,MAAM,CAEf;CACF"}
package/dist/crdt.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Lightweight CRDT primitives for offline-first sync.
3
+ * No external dependencies — pure TypeScript.
4
+ */
5
+ /**
6
+ * Merge two LWW documents — field-level last-write-wins.
7
+ * The field with the higher lamport wins; on ties, higher clientId wins.
8
+ */
9
+ export function mergeLWW(local, remote) {
10
+ const result = { ...local };
11
+ for (const key of Object.keys(remote)) {
12
+ const remoteField = remote[key];
13
+ const localField = local[key];
14
+ if (!localField) {
15
+ result[key] = remoteField;
16
+ }
17
+ else if (remoteField.lamport > localField.lamport) {
18
+ result[key] = remoteField;
19
+ }
20
+ else if (remoteField.lamport === localField.lamport &&
21
+ remoteField.clientId > localField.clientId) {
22
+ result[key] = remoteField;
23
+ }
24
+ // else: local wins — no change
25
+ }
26
+ return result;
27
+ }
28
+ /** Convert a plain record to an LWW document */
29
+ export function toDocument(data, lamport, clientId) {
30
+ const doc = {};
31
+ for (const [key, value] of Object.entries(data)) {
32
+ doc[key] = { value, lamport, clientId };
33
+ }
34
+ return doc;
35
+ }
36
+ /** Extract plain data from an LWW document */
37
+ export function fromDocument(doc) {
38
+ const data = {};
39
+ for (const [key, field] of Object.entries(doc)) {
40
+ data[key] = field.value;
41
+ }
42
+ return data;
43
+ }
44
+ export function orSetAdd(set, value) {
45
+ return [...set, { value, uid: crypto.randomUUID(), removed: false }];
46
+ }
47
+ export function orSetRemove(set, uid) {
48
+ return set.map((el) => (el.uid === uid ? { ...el, removed: true } : el));
49
+ }
50
+ export function orSetMerge(local, remote) {
51
+ const merged = new Map();
52
+ for (const el of [...local, ...remote]) {
53
+ const existing = merged.get(el.uid);
54
+ // Once removed in either replica, it's removed everywhere
55
+ if (!existing || el.removed) {
56
+ merged.set(el.uid, el);
57
+ }
58
+ }
59
+ return Array.from(merged.values()).filter((el) => !el.removed);
60
+ }
61
+ export function orSetValues(set) {
62
+ return set.filter((el) => !el.removed).map((el) => el.value);
63
+ }
64
+ // ── Lamport Clock ─────────────────────────────────────────────────────────────
65
+ export class LamportClock {
66
+ value;
67
+ clientId;
68
+ constructor(clientId, initial = 0) {
69
+ this.value = initial;
70
+ this.clientId = clientId;
71
+ }
72
+ /** Increment and return the new value */
73
+ tick() {
74
+ return ++this.value;
75
+ }
76
+ /** Update from a received timestamp, then increment */
77
+ update(received) {
78
+ this.value = Math.max(this.value, received) + 1;
79
+ return this.value;
80
+ }
81
+ get current() {
82
+ return this.value;
83
+ }
84
+ get id() {
85
+ return this.clientId;
86
+ }
87
+ }
@@ -0,0 +1,46 @@
1
+ import type { Hono } from 'hono';
2
+ export interface ExtensionContext {
3
+ db: any;
4
+ auth: any;
5
+ fieldTypeRegistry: FieldTypeRegistryAPI;
6
+ }
7
+ export interface FieldTypeRegistryAPI {
8
+ register(definition: any): void;
9
+ get(type: string): any;
10
+ has(type: string): boolean;
11
+ list(): string[];
12
+ }
13
+ export interface ZveltioExtension {
14
+ name: string;
15
+ category: string;
16
+ register: (app: Hono, ctx: ExtensionContext) => Promise<void>;
17
+ registerFieldTypes?: (registry: FieldTypeRegistryAPI) => void;
18
+ getMigrations?: () => string[];
19
+ }
20
+ export interface StudioExtensionAPI {
21
+ registerRoute(route: StudioRoute): void;
22
+ registerFieldType(ft: StudioFieldType): void;
23
+ engineUrl: string;
24
+ }
25
+ export interface StudioRoute {
26
+ path: string;
27
+ component: any;
28
+ label: string;
29
+ icon: string;
30
+ category: string;
31
+ children?: StudioRoute[];
32
+ }
33
+ export interface StudioFieldType {
34
+ type: string;
35
+ editor: () => Promise<{
36
+ default: any;
37
+ }>;
38
+ display: () => Promise<{
39
+ default: any;
40
+ }>;
41
+ filter?: () => Promise<{
42
+ default: any;
43
+ }>;
44
+ }
45
+ export type { Hono };
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extension/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAIjC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,GAAG,CAAC;IACR,IAAI,EAAE,GAAG,CAAC;IACV,iBAAiB,EAAE,oBAAoB,CAAC;CACzC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI,CAAC;IAChC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC;IACvB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,IAAI,IAAI,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC9D,aAAa,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;CAChC;AAGD,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACxC,iBAAiB,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,GAAG,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED,YAAY,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ export { ZveltioClient, createZveltioClient } from './client.js';
2
+ export type { ZveltioClientConfig, ListParams, ListResult } from './client.js';
3
+ export { ZveltioRealtime } from './realtime.js';
4
+ export { LocalStore } from './local-store.js';
5
+ export { SyncManager } from './sync-manager.js';
6
+ export type { ZveltioExtension } from './extension/index.js';
7
+ export type { LocalRecord, SyncQueueItem } from './local-store.js';
8
+ export type { SyncManagerConfig } from './sync-manager.js';
9
+ export { useSyncCollection, useSyncStatus } from './svelte.js';
10
+ export * from './crdt.js';
11
+ export * from './core.js';
12
+ export { watchSchema, generateTypes } from './schema-watcher.js';
13
+ export type { CollectionSchema, CollectionField, WatchSchemaOptions } from './schema-watcher.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,mBAAmB,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACnE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAG1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { ZveltioClient, createZveltioClient } from './client.js';
2
+ export { ZveltioRealtime } from './realtime.js';
3
+ export { LocalStore } from './local-store.js';
4
+ export { SyncManager } from './sync-manager.js';
5
+ export { useSyncCollection, useSyncStatus } from './svelte.js';
6
+ export * from './crdt.js';
7
+ export * from './core.js';
8
+ // Schema watcher (for dev mode type generation)
9
+ export { watchSchema, generateTypes } from './schema-watcher.js';
@@ -0,0 +1,68 @@
1
+ import { type LWWDocument } from './crdt.js';
2
+ export interface LocalRecord {
3
+ id: string;
4
+ collection: string;
5
+ data: Record<string, any>;
6
+ _localVersion: number;
7
+ _serverVersion: number;
8
+ _syncStatus: 'synced' | 'pending' | 'conflict';
9
+ _updatedAt: number;
10
+ _deletedAt?: number;
11
+ _conflictData?: Record<string, any>;
12
+ _crdtDoc?: LWWDocument;
13
+ }
14
+ export interface SyncQueueItem {
15
+ id: string;
16
+ collection: string;
17
+ recordId: string;
18
+ operation: 'create' | 'update' | 'delete';
19
+ payload: Record<string, any>;
20
+ attempts: number;
21
+ createdAt: number;
22
+ lastAttemptAt?: number;
23
+ error?: string;
24
+ }
25
+ export interface OfflineBlob {
26
+ id: string;
27
+ blob: Blob;
28
+ collection: string;
29
+ recordId: string;
30
+ field: string;
31
+ createdAt: number;
32
+ }
33
+ export declare class LocalStore {
34
+ private db;
35
+ private clock;
36
+ open(): Promise<void>;
37
+ private persistLamport;
38
+ /** Write a local record and add to sync queue */
39
+ put(collection: string, id: string, data: Record<string, any>): Promise<LocalRecord>;
40
+ /** Read a local record (instant, no network) */
41
+ get(collection: string, id: string): Promise<LocalRecord | undefined>;
42
+ /** List records from a collection (local) */
43
+ list(collection: string): Promise<LocalRecord[]>;
44
+ /** Soft delete local and add to sync queue */
45
+ delete(collection: string, id: string): Promise<void>;
46
+ /** Returns all pending operations from sync queue */
47
+ getPendingOps(): Promise<SyncQueueItem[]>;
48
+ /** Mark an operation as completed (remove from queue, update record status) */
49
+ markSynced(queueItemId: string, collection: string, recordId: string, serverVersion: number): Promise<void>;
50
+ /** Mark an operation as failed (increment attempts, save error) */
51
+ markFailed(queueItemId: string, error: string): Promise<void>;
52
+ /** Apply data from server (via WebSocket or pull) */
53
+ applyServerUpdate(collection: string, id: string, data: Record<string, any>, serverVersion: number): Promise<void>;
54
+ /** Get records with conflicts for UI resolution */
55
+ getConflicts(collection?: string): Promise<LocalRecord[]>;
56
+ /** Resolve a conflict (user decides which version wins) */
57
+ resolveConflict(collection: string, id: string, resolvedData: Record<string, any>): Promise<void>;
58
+ /** Save an offline blob — returns a temporary ID 'local_blob_<UUID>' */
59
+ saveBlob(blob: Blob, collection: string, recordId: string, field: string): Promise<string>;
60
+ /** Returns all pending offline blobs */
61
+ getPendingBlobs(): Promise<OfflineBlob[]>;
62
+ /** Delete an offline blob after successful upload */
63
+ deleteBlob(id: string): Promise<void>;
64
+ /** Clear all local data */
65
+ clear(): Promise<void>;
66
+ close(): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=local-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["../src/local-store.ts"],"names":[],"mappings":"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"}