@zveltio/sdk 2.0.8 → 3.0.0-beta.1

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 (64) hide show
  1. package/dist/build/index.d.ts +77 -0
  2. package/dist/build/index.d.ts.map +1 -0
  3. package/dist/build/index.js +157 -0
  4. package/dist/client/QueryBuilder.d.ts.map +1 -1
  5. package/dist/client/QueryBuilder.js +12 -3
  6. package/dist/client/RealtimeClient.d.ts.map +1 -1
  7. package/dist/client/RealtimeClient.js +3 -1
  8. package/dist/client/ZveltioClient.d.ts.map +1 -1
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +16 -5
  11. package/dist/core.d.ts.map +1 -1
  12. package/dist/ddl/index.d.ts +153 -0
  13. package/dist/ddl/index.d.ts.map +1 -0
  14. package/dist/ddl/index.js +33 -0
  15. package/dist/extension/index.d.ts +522 -10
  16. package/dist/extension/index.d.ts.map +1 -1
  17. package/dist/extension/index.js +1 -1
  18. package/dist/extension/permission-gate.d.ts +37 -0
  19. package/dist/extension/permission-gate.d.ts.map +1 -0
  20. package/dist/extension/permission-gate.js +93 -0
  21. package/dist/generate-types.d.ts +37 -0
  22. package/dist/generate-types.d.ts.map +1 -0
  23. package/dist/generate-types.js +157 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/local-store.d.ts.map +1 -1
  27. package/dist/local-store.js +1 -3
  28. package/dist/offline/index.d.ts +105 -0
  29. package/dist/offline/index.d.ts.map +1 -0
  30. package/dist/offline/index.js +235 -0
  31. package/dist/publish/index.d.ts +94 -0
  32. package/dist/publish/index.d.ts.map +1 -0
  33. package/dist/publish/index.js +148 -0
  34. package/dist/realtime.d.ts.map +1 -1
  35. package/dist/realtime.js +6 -2
  36. package/dist/rpc/index.d.ts +68 -0
  37. package/dist/rpc/index.d.ts.map +1 -0
  38. package/dist/rpc/index.js +61 -0
  39. package/dist/schema-codegen.d.ts +60 -0
  40. package/dist/schema-codegen.d.ts.map +1 -0
  41. package/dist/schema-codegen.js +369 -0
  42. package/dist/schema-watcher.d.ts.map +1 -1
  43. package/dist/schema-watcher.js +1 -3
  44. package/dist/studio/index.d.ts +84 -0
  45. package/dist/studio/index.d.ts.map +1 -0
  46. package/dist/studio/index.js +180 -0
  47. package/dist/svelte.d.ts.map +1 -1
  48. package/dist/sync-manager.d.ts.map +1 -1
  49. package/dist/sync-manager.js +4 -11
  50. package/dist/testing/index.d.ts +121 -0
  51. package/dist/testing/index.d.ts.map +1 -0
  52. package/dist/testing/index.js +283 -0
  53. package/dist/testing/with-test-db.d.ts +119 -0
  54. package/dist/testing/with-test-db.d.ts.map +1 -0
  55. package/dist/testing/with-test-db.js +313 -0
  56. package/dist/utils.d.ts.map +1 -1
  57. package/dist/utils.js +3 -1
  58. package/dist/validate/index.d.ts +95 -0
  59. package/dist/validate/index.d.ts.map +1 -0
  60. package/dist/validate/index.js +201 -0
  61. package/dist/validate/migration-parse.d.ts +17 -0
  62. package/dist/validate/migration-parse.d.ts.map +1 -0
  63. package/dist/validate/migration-parse.js +11 -0
  64. package/package.json +53 -1
@@ -0,0 +1,93 @@
1
+ /**
2
+ * `permissionGate` — minimal route-level RBAC for extensions.
3
+ *
4
+ * Default-deny: if no Casbin policy grants the resource/action, the request
5
+ * is rejected with 403. The god role bypasses the gate (handled by the
6
+ * engine's `checkPermission`).
7
+ *
8
+ * Action is derived from the HTTP method using the standard CRUD mapping
9
+ * (GET → read, POST → create, PATCH/PUT → update, DELETE → delete). The
10
+ * `resource` is the extension's logical name (e.g. `'crm'`, `'invoices'`);
11
+ * operators grant access by adding a Casbin policy:
12
+ *
13
+ * INSERT INTO zvd_permissions (ptype, v0, v1, v2)
14
+ * VALUES ('p', 'user_role', 'crm', 'read');
15
+ *
16
+ * Why this exists: many extensions historically only checked
17
+ * `auth.api.getSession(...)` — i.e. any authenticated user could
18
+ * read/write the entire extension. That's the right default for a
19
+ * single-tenant deployment but unacceptable in multi-tenant or
20
+ * role-segmented installs. The gate gives operators a single knob to
21
+ * tighten access without each extension having to roll its own RBAC.
22
+ *
23
+ * Usage in an extension's `routes.ts`:
24
+ *
25
+ * app.use('*', permissionGate(ctx, 'crm'));
26
+ *
27
+ * Place AFTER the auth-guard middleware that sets `c.set('user', ...)`.
28
+ */
29
+ function methodToAction(method) {
30
+ const m = method.toUpperCase();
31
+ switch (m) {
32
+ case 'GET':
33
+ case 'HEAD':
34
+ case 'OPTIONS':
35
+ return 'read';
36
+ case 'POST':
37
+ return 'create';
38
+ case 'PATCH':
39
+ case 'PUT':
40
+ return 'update';
41
+ case 'DELETE':
42
+ return 'delete';
43
+ default:
44
+ // Unknown method → demand the most restrictive action so the gate
45
+ // never fails open.
46
+ return 'delete';
47
+ }
48
+ }
49
+ /**
50
+ * Operators can disable the gate temporarily during a permission backfill
51
+ * by setting `EXTENSION_RBAC=permissive`. When permissive, the gate becomes
52
+ * a no-op (any authenticated user passes) but still logs the denied
53
+ * attempts so the operator can preview what *would* have been blocked
54
+ * before flipping to strict.
55
+ *
56
+ * Strict (default) is the secure posture and what the SDK ships with.
57
+ */
58
+ function rbacMode() {
59
+ const v = globalThis.process?.env?.EXTENSION_RBAC;
60
+ return v === 'permissive' ? 'permissive' : 'strict';
61
+ }
62
+ export function permissionGate(ctx, resource, opts = {}) {
63
+ return async (c, next) => {
64
+ // CORS preflight (OPTIONS) must never be authorization-gated —
65
+ // browsers send it without credentials and a 401/403 here breaks
66
+ // the actual cross-origin request that would follow. The global
67
+ // CORS middleware in the engine returns the preflight response;
68
+ // we just need to let it through.
69
+ if (c.req.method === 'OPTIONS') {
70
+ await next();
71
+ return;
72
+ }
73
+ const user = c.get('user');
74
+ if (!user?.id) {
75
+ return c.json({ error: 'Unauthorized' }, 401);
76
+ }
77
+ const methodKey = c.req.method.toUpperCase();
78
+ const action = opts.actionOverrides?.[methodKey] ?? methodToAction(methodKey);
79
+ const allowed = await ctx.checkPermission(user.id, resource, action);
80
+ if (!allowed) {
81
+ if (rbacMode() === 'permissive') {
82
+ // Audit-log mode: surface what WOULD be blocked so operators can
83
+ // configure policies before flipping to strict. Stays a single
84
+ // console.warn — no DB writes on the hot path.
85
+ console.warn(`[permissionGate] WOULD DENY user=${user.id} ${c.req.method} ${c.req.path} → ${resource}:${action} (EXTENSION_RBAC=permissive — gate bypassed)`);
86
+ await next();
87
+ return;
88
+ }
89
+ return c.json({ error: `Forbidden: missing ${resource}:${action} permission` }, 403);
90
+ }
91
+ await next();
92
+ };
93
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * generate-types.ts
3
+ *
4
+ * Programmatic type generator for Zveltio collections.
5
+ * Fetches collection schemas from a running Zveltio engine and generates
6
+ * TypeScript interface declarations with full type information.
7
+ *
8
+ * Usage (programmatic):
9
+ * import { generateTypes } from '@zveltio/sdk/generate-types';
10
+ * const dts = await generateTypes({ url: 'http://localhost:3000', apiKey: 'sk-...' });
11
+ * await fs.writeFile('./types/zveltio.d.ts', dts);
12
+ *
13
+ * Usage (CLI):
14
+ * zveltio generate-types --output ./types/zveltio.d.ts
15
+ * zveltio types --output ./types/zveltio.d.ts
16
+ */
17
+ export interface GenerateTypesOptions {
18
+ /** Zveltio engine URL (default: http://localhost:3000) */
19
+ url?: string;
20
+ /** API key for authentication */
21
+ apiKey?: string;
22
+ /** Only generate types for this collection */
23
+ collection?: string;
24
+ }
25
+ /**
26
+ * Generates TypeScript declarations for all (or one) Zveltio collection(s).
27
+ *
28
+ * Returns the `.d.ts` file content as a string. The caller is responsible for
29
+ * writing it to disk.
30
+ *
31
+ * The engine endpoint `/api/admin/types` is used when available (returns
32
+ * pre-generated TS from the server's field-type registry). If the endpoint
33
+ * fails or is unavailable this function falls back to generating types from
34
+ * the collection schema returned by `/api/collections`.
35
+ */
36
+ export declare function generateTypes(options?: GenerateTypesOptions): Promise<string>;
37
+ //# sourceMappingURL=generate-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-types.d.ts","sourceRoot":"","sources":["../src/generate-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0HH,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyDvF"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * generate-types.ts
3
+ *
4
+ * Programmatic type generator for Zveltio collections.
5
+ * Fetches collection schemas from a running Zveltio engine and generates
6
+ * TypeScript interface declarations with full type information.
7
+ *
8
+ * Usage (programmatic):
9
+ * import { generateTypes } from '@zveltio/sdk/generate-types';
10
+ * const dts = await generateTypes({ url: 'http://localhost:3000', apiKey: 'sk-...' });
11
+ * await fs.writeFile('./types/zveltio.d.ts', dts);
12
+ *
13
+ * Usage (CLI):
14
+ * zveltio generate-types --output ./types/zveltio.d.ts
15
+ * zveltio types --output ./types/zveltio.d.ts
16
+ */
17
+ // ── Field type → TypeScript type mapping ─────────────────────────────────────
18
+ const FIELD_TYPE_MAP = {
19
+ text: 'string',
20
+ textarea: 'string',
21
+ richtext: 'string',
22
+ markdown: 'string',
23
+ email: 'string',
24
+ url: 'string',
25
+ password: 'string',
26
+ slug: 'string',
27
+ uuid: 'string',
28
+ color: 'string',
29
+ number: 'number',
30
+ integer: 'number',
31
+ decimal: 'number',
32
+ float: 'number',
33
+ currency: 'number',
34
+ percentage: 'number',
35
+ boolean: 'boolean',
36
+ toggle: 'boolean',
37
+ date: 'string', // ISO 8601 date string
38
+ datetime: 'string', // ISO 8601 datetime string
39
+ time: 'string',
40
+ timestamp: 'string',
41
+ json: 'Record<string, unknown>',
42
+ jsonb: 'Record<string, unknown>',
43
+ object: 'Record<string, unknown>',
44
+ array: 'unknown[]',
45
+ tags: 'string[]',
46
+ select: 'string',
47
+ multiselect: 'string[]',
48
+ relation: 'string', // UUID of related record
49
+ file: 'string', // storage path
50
+ image: 'string', // storage path
51
+ point: '{ lat: number; lng: number }',
52
+ geojson: 'GeoJSON.Geometry',
53
+ };
54
+ function fieldTypeToTs(fieldType) {
55
+ return FIELD_TYPE_MAP[fieldType.toLowerCase()] ?? 'unknown';
56
+ }
57
+ /**
58
+ * Converts a collection name (snake_case) to a PascalCase interface name.
59
+ * Examples: "my_orders" → "MyOrders", "products" → "Products"
60
+ */
61
+ function toPascalCase(name) {
62
+ return name
63
+ .split(/[_\s-]+/)
64
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
65
+ .join('');
66
+ }
67
+ function generateCollectionInterface(col) {
68
+ const interfaceName = toPascalCase(col.name) + 'Collection';
69
+ const fields = col.fields ?? [];
70
+ const lines = [];
71
+ if (col.description) {
72
+ lines.push(`/** ${col.description} */`);
73
+ }
74
+ lines.push(`export interface ${interfaceName} {`);
75
+ lines.push(` /** Internal record ID (UUID) */`);
76
+ lines.push(` id: string;`);
77
+ for (const field of fields) {
78
+ if (field.description) {
79
+ lines.push(` /** ${field.description} */`);
80
+ }
81
+ const tsType = fieldTypeToTs(field.type);
82
+ const optional = field.required ? '' : '?';
83
+ lines.push(` ${field.name}${optional}: ${tsType};`);
84
+ }
85
+ lines.push(` /** ISO 8601 creation timestamp */`);
86
+ lines.push(` created_at: string;`);
87
+ lines.push(` /** ISO 8601 last-updated timestamp */`);
88
+ lines.push(` updated_at: string;`);
89
+ lines.push(`}`);
90
+ return lines.join('\n');
91
+ }
92
+ function generateIndexInterface(collections) {
93
+ const lines = [];
94
+ lines.push(`/** Index of all Zveltio collections — use with \`ZveltioClient<ZveltioCollections>\` */`);
95
+ lines.push(`export interface ZveltioCollections {`);
96
+ for (const col of collections) {
97
+ const interfaceName = toPascalCase(col.name) + 'Collection';
98
+ const comment = col.display_name || col.name;
99
+ lines.push(` /** ${comment} */`);
100
+ lines.push(` ${col.name}: ${interfaceName};`);
101
+ }
102
+ lines.push(`}`);
103
+ return lines.join('\n');
104
+ }
105
+ /**
106
+ * Generates TypeScript declarations for all (or one) Zveltio collection(s).
107
+ *
108
+ * Returns the `.d.ts` file content as a string. The caller is responsible for
109
+ * writing it to disk.
110
+ *
111
+ * The engine endpoint `/api/admin/types` is used when available (returns
112
+ * pre-generated TS from the server's field-type registry). If the endpoint
113
+ * fails or is unavailable this function falls back to generating types from
114
+ * the collection schema returned by `/api/collections`.
115
+ */
116
+ export async function generateTypes(options = {}) {
117
+ const engineUrl = (options.url ?? process.env.ZVELTIO_URL ?? 'http://localhost:3000').replace(/\/$/, '');
118
+ const apiKey = options.apiKey ?? process.env.ZVELTIO_API_KEY ?? '';
119
+ const headers = {};
120
+ if (apiKey)
121
+ headers['Authorization'] = `Bearer ${apiKey}`;
122
+ // ── Try fast path: engine pre-generates types ────────────────────────────
123
+ try {
124
+ const path = options.collection
125
+ ? `/api/admin/types/${encodeURIComponent(options.collection)}`
126
+ : '/api/admin/types';
127
+ const res = await fetch(`${engineUrl}${path}`, { headers });
128
+ if (res.ok) {
129
+ return await res.text();
130
+ }
131
+ // Fall through to manual generation on 404/401/500
132
+ if (res.status !== 404) {
133
+ console.warn(`[generate-types] Engine /api/admin/types returned ${res.status}, falling back to manual generation.`);
134
+ }
135
+ }
136
+ catch {
137
+ // Engine unreachable or endpoint absent — use fallback
138
+ }
139
+ // ── Fallback: fetch schema and generate types locally ────────────────────
140
+ const collectionsUrl = options.collection
141
+ ? `${engineUrl}/api/collections/${encodeURIComponent(options.collection)}`
142
+ : `${engineUrl}/api/collections`;
143
+ const res = await fetch(collectionsUrl, { headers });
144
+ if (!res.ok) {
145
+ throw new Error(`Failed to fetch collections from ${collectionsUrl}: ${res.status} ${res.statusText}`);
146
+ }
147
+ const data = (await res.json());
148
+ const collections = Array.isArray(data.collections)
149
+ ? data.collections.filter((c) => !c.is_system)
150
+ : data.collection
151
+ ? [data.collection]
152
+ : [];
153
+ const header = `// Auto-generated by Zveltio\n// Do not edit manually — run: zveltio generate-types\n// Generated: ${new Date().toISOString()}\n`;
154
+ const interfaces = collections.map(generateCollectionInterface).join('\n\n');
155
+ const indexType = options.collection ? '' : '\n\n' + generateIndexInterface(collections);
156
+ return `${header}\n${interfaces}${indexType}\n`;
157
+ }
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { ZveltioClientConfig, ListParams, ListResult } from './client.js';
3
3
  export { ZveltioRealtime } from './realtime.js';
4
4
  export { LocalStore } from './local-store.js';
5
5
  export { SyncManager } from './sync-manager.js';
6
- export type { ZveltioExtension } from './extension/index.js';
6
+ export type { ZveltioExtension, ExtensionContext, FieldTypeRegistryAPI, StudioExtensionAPI, StudioRoute, StudioFieldType, AssetPreviewHandler, } from './extension/index.js';
7
7
  export type { LocalRecord, SyncQueueItem } from './local-store.js';
8
8
  export type { SyncManagerConfig } from './sync-manager.js';
9
9
  export { useSyncCollection, useSyncStatus } from './svelte.js';
@@ -1 +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"}
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,EACV,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["../src/local-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAG/F,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAA6B;IACvC,OAAO,CAAC,KAAK,CAA6B;IAEpC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAuCb,cAAc;IAK5B,iDAAiD;IAC3C,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,OAAO,CAAC,WAAW,CAAC;IA4CvB,gDAAgD;IAC1C,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAS3E,6CAA6C;IACvC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAUtD,8CAA8C;IACxC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B3D,qDAAqD;IAC/C,aAAa,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAO/C,+EAA+E;IACzE,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IAqBhB,mEAAmE;IAC7D,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAanE,qDAAqD;IAC/C,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC;IA8ChB,mDAAmD;IAC7C,YAAY,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAW/D,2DAA2D;IACrD,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC;IAahB,wEAAwE;IAClE,QAAQ,CACZ,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IAelB,wCAAwC;IAClC,eAAe,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAK/C,qDAAqD;IAC/C,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3C,2BAA2B;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
1
+ {"version":3,"file":"local-store.d.ts","sourceRoot":"","sources":["../src/local-store.ts"],"names":[],"mappings":"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"}
@@ -134,9 +134,7 @@ export class LocalStore {
134
134
  // Remove from queue
135
135
  await tx.objectStore('sync_queue').delete(queueItemId);
136
136
  // Update record status
137
- const record = (await tx
138
- .objectStore('records')
139
- .get([collection, recordId]));
137
+ const record = (await tx.objectStore('records').get([collection, recordId]));
140
138
  if (record) {
141
139
  record._serverVersion = serverVersion;
142
140
  record._syncStatus = 'synced';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Offline-sync provider abstraction.
3
+ *
4
+ * The SDK ships TWO sync strategies for the Postgres ↔ client-SQLite
5
+ * loop, picked at construction time:
6
+ *
7
+ * - **`crdt`** (default) — bespoke field-level Last-Write-Wins merge on
8
+ * top of IndexedDB / SQLite via `LocalStore` + `SyncManager`. Works
9
+ * offline, applies on reconnect, handles conflicts. No external
10
+ * dependency beyond the SDK + engine.
11
+ *
12
+ * - **`electric`** — Electric SQL replication. Postgres logical-
13
+ * replication slot → Electric service → client. Conflicts are
14
+ * resolved server-side via CRDTs Electric implements internally.
15
+ * Requires an Electric service running alongside Postgres + the
16
+ * engine routes from `routes/electric.ts` that mint short-lived
17
+ * JWTs the client uses to authenticate.
18
+ *
19
+ * Auth flow for the electric provider
20
+ * -----------------------------------
21
+ * 1. Provider calls `POST {engineUrl}/api/electric/auth` (with
22
+ * credentials; the engine reads the better-auth session).
23
+ * 2. Engine returns `{ token, expiresAt, electricUrl }`.
24
+ * 3. Provider opens a websocket to `${electricUrl}?token=<jwt>`.
25
+ * 4. Before each token expires, the provider re-requests a fresh one
26
+ * (no user interaction).
27
+ *
28
+ * Pull/push semantics
29
+ * -------------------
30
+ * Electric replicates continuously over the websocket — there's no
31
+ * explicit "pull" call. The provider's `pull()` is a no-op that returns
32
+ * once the initial sync handshake completes. `push()` is the same: local
33
+ * writes go through Electric's normal replication path automatically.
34
+ * Both shims exist so the OfflineProvider interface stays uniform across
35
+ * crdt/electric.
36
+ *
37
+ * Subscriptions
38
+ * -------------
39
+ * `subscribe('zvd_contacts', cb)` registers a callback fired whenever
40
+ * Electric pushes a change for that table. The implementation uses the
41
+ * websocket's `message` event with a routing table; one socket, many
42
+ * subscriptions.
43
+ */
44
+ export type OfflineProviderKind = 'crdt' | 'electric';
45
+ export interface OfflineProviderConfig {
46
+ /** Which sync engine to use. Default: 'crdt'. */
47
+ provider?: OfflineProviderKind;
48
+ /** Engine base URL — used for the token-mint call AND CRDT push/pull. */
49
+ engineUrl: string;
50
+ /**
51
+ * Override the Electric websocket URL. When omitted, the provider asks
52
+ * the engine via `GET /api/electric/config` so operators have a single
53
+ * place to configure it. Useful for tests + dev setups.
54
+ */
55
+ electricUrl?: string;
56
+ /**
57
+ * Tables to record on the audit token claim. Does NOT restrict what
58
+ * Electric replicates — Electric enforces that via its own config.
59
+ */
60
+ tables?: string[];
61
+ /**
62
+ * Override the `fetch` impl — handy for tests + SSR. Default: globalThis.fetch.
63
+ */
64
+ fetch?: typeof fetch;
65
+ /**
66
+ * Override the WebSocket constructor — handy for tests + non-browser
67
+ * runtimes. Default: globalThis.WebSocket.
68
+ */
69
+ websocket?: typeof WebSocket;
70
+ }
71
+ /**
72
+ * Public interface every provider implements. Today the CRDT provider
73
+ * wraps `SyncManager`; the Electric provider wraps a websocket. Both
74
+ * expose the same surface.
75
+ */
76
+ export interface OfflineProvider {
77
+ readonly kind: OfflineProviderKind;
78
+ /** Pull remote rows into the local store. Cheap to call repeatedly. */
79
+ pull(): Promise<void>;
80
+ /** Push local mutations to the server. Returns the number of ops sent. */
81
+ push(): Promise<number>;
82
+ /** Subscribe to live changes on a table. Returns an unsubscribe fn. */
83
+ subscribe(table: string, cb: (rows: unknown[]) => void): () => void;
84
+ /** Stop background sync + release resources. */
85
+ close(): Promise<void>;
86
+ }
87
+ export declare class ElectricNotConfigured extends Error {
88
+ constructor(reason: string);
89
+ }
90
+ export declare class ElectricUnavailable extends Error {
91
+ constructor(reason: string);
92
+ }
93
+ /**
94
+ * Build the configured offline-sync provider.
95
+ *
96
+ * For `crdt` (default): returns a thin adapter around `SyncManager`.
97
+ *
98
+ * For `electric`: returns the real Electric provider. Throws
99
+ * `ElectricNotConfigured` synchronously when required config is missing
100
+ * (no electricUrl AND no way to discover one), or `ElectricUnavailable`
101
+ * lazily on the first network op when the engine reports Electric is
102
+ * disabled.
103
+ */
104
+ export declare function createOfflineProvider(config: OfflineProviderConfig): Promise<OfflineProvider>;
105
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/offline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,qBAAqB;IACpC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,SAAS,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,uEAAuE;IACvE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,0EAA0E;IAC1E,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,uEAAuE;IACvE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACpE,gDAAgD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,MAAM,EAAE,MAAM;CAQ3B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,MAAM,EAAE,MAAM;CAQ3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,eAAe,CAAC,CAQ1B"}