@pol-studios/powersync 1.0.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.
- package/dist/attachments/index.d.ts +399 -0
- package/dist/attachments/index.js +16 -0
- package/dist/attachments/index.js.map +1 -0
- package/dist/chunk-32OLICZO.js +1 -0
- package/dist/chunk-32OLICZO.js.map +1 -0
- package/dist/chunk-4FJVBR3X.js +227 -0
- package/dist/chunk-4FJVBR3X.js.map +1 -0
- package/dist/chunk-7BPTGEVG.js +1 -0
- package/dist/chunk-7BPTGEVG.js.map +1 -0
- package/dist/chunk-7JQZBZ5N.js +1 -0
- package/dist/chunk-7JQZBZ5N.js.map +1 -0
- package/dist/chunk-BJ36QDFN.js +290 -0
- package/dist/chunk-BJ36QDFN.js.map +1 -0
- package/dist/chunk-CFCK2LHI.js +1002 -0
- package/dist/chunk-CFCK2LHI.js.map +1 -0
- package/dist/chunk-CHRTN5PF.js +322 -0
- package/dist/chunk-CHRTN5PF.js.map +1 -0
- package/dist/chunk-FLHDT4TS.js +327 -0
- package/dist/chunk-FLHDT4TS.js.map +1 -0
- package/dist/chunk-GBGATW2S.js +749 -0
- package/dist/chunk-GBGATW2S.js.map +1 -0
- package/dist/chunk-NPNBGCRC.js +65 -0
- package/dist/chunk-NPNBGCRC.js.map +1 -0
- package/dist/chunk-Q3LFFMRR.js +925 -0
- package/dist/chunk-Q3LFFMRR.js.map +1 -0
- package/dist/chunk-T225XEML.js +298 -0
- package/dist/chunk-T225XEML.js.map +1 -0
- package/dist/chunk-W7HSR35B.js +1 -0
- package/dist/chunk-W7HSR35B.js.map +1 -0
- package/dist/connector/index.d.ts +5 -0
- package/dist/connector/index.js +14 -0
- package/dist/connector/index.js.map +1 -0
- package/dist/core/index.d.ts +197 -0
- package/dist/core/index.js +96 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-nae7nzib.d.ts +147 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +191 -0
- package/dist/index.js.map +1 -0
- package/dist/index.native.d.ts +14 -0
- package/dist/index.native.js +195 -0
- package/dist/index.native.js.map +1 -0
- package/dist/index.web.d.ts +14 -0
- package/dist/index.web.js +195 -0
- package/dist/index.web.js.map +1 -0
- package/dist/platform/index.d.ts +280 -0
- package/dist/platform/index.js +14 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.native.d.ts +37 -0
- package/dist/platform/index.native.js +7 -0
- package/dist/platform/index.native.js.map +1 -0
- package/dist/platform/index.web.d.ts +37 -0
- package/dist/platform/index.web.js +7 -0
- package/dist/platform/index.web.js.map +1 -0
- package/dist/provider/index.d.ts +873 -0
- package/dist/provider/index.js +63 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/supabase-connector-D14-kl5v.d.ts +232 -0
- package/dist/sync/index.d.ts +421 -0
- package/dist/sync/index.js +14 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/types-Cd7RhNqf.d.ts +224 -0
- package/dist/types-afHtE1U_.d.ts +391 -0
- package/package.json +101 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifySupabaseError
|
|
3
|
+
} from "./chunk-CHRTN5PF.js";
|
|
4
|
+
|
|
5
|
+
// src/connector/types.ts
|
|
6
|
+
var defaultSchemaRouter = () => "public";
|
|
7
|
+
|
|
8
|
+
// src/connector/supabase-connector.ts
|
|
9
|
+
var SupabaseConnector = class {
|
|
10
|
+
supabase;
|
|
11
|
+
powerSyncUrl;
|
|
12
|
+
schemaRouter;
|
|
13
|
+
crudHandler;
|
|
14
|
+
logger;
|
|
15
|
+
onTransactionSuccess;
|
|
16
|
+
onTransactionFailure;
|
|
17
|
+
onTransactionComplete;
|
|
18
|
+
shouldUploadFn;
|
|
19
|
+
// Active project IDs for scoped sync (optional feature)
|
|
20
|
+
activeProjectIds = [];
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.supabase = options.supabaseClient;
|
|
23
|
+
this.powerSyncUrl = options.powerSyncUrl;
|
|
24
|
+
this.schemaRouter = options.schemaRouter ?? defaultSchemaRouter;
|
|
25
|
+
this.crudHandler = options.crudHandler;
|
|
26
|
+
this.logger = options.logger;
|
|
27
|
+
this.onTransactionSuccess = options.onTransactionSuccess;
|
|
28
|
+
this.onTransactionFailure = options.onTransactionFailure;
|
|
29
|
+
this.onTransactionComplete = options.onTransactionComplete;
|
|
30
|
+
this.shouldUploadFn = options.shouldUpload;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set the active project IDs for scoped sync.
|
|
34
|
+
* Call this when user selects/opens projects.
|
|
35
|
+
*/
|
|
36
|
+
setActiveProjectIds(projectIds) {
|
|
37
|
+
this.activeProjectIds = projectIds;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the current active project IDs.
|
|
41
|
+
*/
|
|
42
|
+
getActiveProjectIds() {
|
|
43
|
+
return this.activeProjectIds;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get credentials for PowerSync connection.
|
|
47
|
+
* Uses Supabase session token.
|
|
48
|
+
*
|
|
49
|
+
* Note: Token refresh is handled by Supabase's startAutoRefresh() which must be
|
|
50
|
+
* called on app initialization. getSession() returns the auto-refreshed token.
|
|
51
|
+
*/
|
|
52
|
+
async fetchCredentials() {
|
|
53
|
+
this.logger?.debug("[Connector] Fetching credentials...");
|
|
54
|
+
const {
|
|
55
|
+
data: { session },
|
|
56
|
+
error
|
|
57
|
+
} = await this.supabase.auth.getSession();
|
|
58
|
+
if (error) {
|
|
59
|
+
this.logger?.error("[Connector] Auth error:", error);
|
|
60
|
+
throw new Error(`Failed to get Supabase session: ${error.message}`);
|
|
61
|
+
}
|
|
62
|
+
if (!session) {
|
|
63
|
+
this.logger?.error("[Connector] No active session");
|
|
64
|
+
throw new Error("No active Supabase session");
|
|
65
|
+
}
|
|
66
|
+
this.logger?.debug(
|
|
67
|
+
"[Connector] Credentials fetched, token expires at:",
|
|
68
|
+
session.expires_at
|
|
69
|
+
);
|
|
70
|
+
return {
|
|
71
|
+
endpoint: this.powerSyncUrl,
|
|
72
|
+
token: session.access_token,
|
|
73
|
+
expiresAt: session.expires_at ? new Date(session.expires_at * 1e3) : void 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Upload local changes to Supabase.
|
|
78
|
+
* Called automatically by PowerSync when there are pending uploads.
|
|
79
|
+
*/
|
|
80
|
+
async uploadData(database) {
|
|
81
|
+
if (this.shouldUploadFn && !this.shouldUploadFn()) {
|
|
82
|
+
if (__DEV__) {
|
|
83
|
+
console.log("[Connector] Upload skipped - sync mode does not allow uploads");
|
|
84
|
+
}
|
|
85
|
+
this.logger?.debug("[Connector] Upload skipped - sync mode does not allow uploads");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (__DEV__) {
|
|
89
|
+
console.log("[Connector] uploadData called, fetching next CRUD transaction...");
|
|
90
|
+
}
|
|
91
|
+
const transaction = await database.getNextCrudTransaction();
|
|
92
|
+
if (!transaction) {
|
|
93
|
+
if (__DEV__) {
|
|
94
|
+
console.log("[Connector] No pending CRUD transaction found");
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (__DEV__) {
|
|
99
|
+
console.log("[Connector] Transaction fetched:", {
|
|
100
|
+
crudCount: transaction.crud.length,
|
|
101
|
+
entries: transaction.crud.map((e) => ({
|
|
102
|
+
table: e.table,
|
|
103
|
+
op: e.op,
|
|
104
|
+
id: e.id,
|
|
105
|
+
opDataKeys: e.opData ? Object.keys(e.opData) : []
|
|
106
|
+
}))
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
for (const entry of transaction.crud) {
|
|
111
|
+
if (__DEV__) {
|
|
112
|
+
console.log("[Connector] Processing CRUD entry:", {
|
|
113
|
+
table: entry.table,
|
|
114
|
+
op: entry.op,
|
|
115
|
+
id: entry.id,
|
|
116
|
+
opData: entry.opData
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
await this.processCrudEntry(entry);
|
|
120
|
+
}
|
|
121
|
+
if (__DEV__) {
|
|
122
|
+
console.log("[Connector] All CRUD entries processed, completing transaction...");
|
|
123
|
+
}
|
|
124
|
+
await transaction.complete();
|
|
125
|
+
if (__DEV__) {
|
|
126
|
+
console.log("[Connector] Transaction completed successfully:", {
|
|
127
|
+
entriesCount: transaction.crud.length
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
this.onTransactionSuccess?.(transaction.crud);
|
|
131
|
+
this.onTransactionComplete?.(transaction.crud);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
const classified = classifySupabaseError(error);
|
|
134
|
+
console.error("[PowerSync Connector] Upload FAILED:", {
|
|
135
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
136
|
+
errorKeys: error && typeof error === "object" ? Object.keys(error) : [],
|
|
137
|
+
errorObject: JSON.stringify(error, null, 2),
|
|
138
|
+
// Full structure
|
|
139
|
+
classified,
|
|
140
|
+
isPermanent: classified.isPermanent,
|
|
141
|
+
// Explicitly log this
|
|
142
|
+
entries: transaction.crud.map((e) => ({
|
|
143
|
+
table: e.table,
|
|
144
|
+
op: e.op,
|
|
145
|
+
id: e.id
|
|
146
|
+
}))
|
|
147
|
+
});
|
|
148
|
+
this.logger?.error("[Connector] Upload error:", {
|
|
149
|
+
error,
|
|
150
|
+
classified,
|
|
151
|
+
entries: transaction.crud.map((e) => ({ table: e.table, op: e.op, id: e.id }))
|
|
152
|
+
});
|
|
153
|
+
this.onTransactionFailure?.(
|
|
154
|
+
transaction.crud,
|
|
155
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
156
|
+
classified
|
|
157
|
+
);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Process a single CRUD operation.
|
|
163
|
+
*
|
|
164
|
+
* UUID-native tables (public schema, post-migration) use `id` as the UUID column.
|
|
165
|
+
* Core schema tables (Profile, Comment, CommentSection) still use a separate `uuid` column.
|
|
166
|
+
*/
|
|
167
|
+
/**
|
|
168
|
+
* Process a single CRUD operation.
|
|
169
|
+
*
|
|
170
|
+
* All synced tables use `id` as their UUID primary key column.
|
|
171
|
+
*/
|
|
172
|
+
async processCrudEntry(entry) {
|
|
173
|
+
const table = entry.table;
|
|
174
|
+
const id = entry.id;
|
|
175
|
+
const schema = this.schemaRouter(table);
|
|
176
|
+
if (this.crudHandler) {
|
|
177
|
+
let handled = false;
|
|
178
|
+
switch (entry.op) {
|
|
179
|
+
case "PUT" /* PUT */:
|
|
180
|
+
handled = await this.crudHandler.handlePut?.(entry, this.supabase, schema) ?? false;
|
|
181
|
+
break;
|
|
182
|
+
case "PATCH" /* PATCH */:
|
|
183
|
+
handled = await this.crudHandler.handlePatch?.(
|
|
184
|
+
entry,
|
|
185
|
+
this.supabase,
|
|
186
|
+
schema
|
|
187
|
+
) ?? false;
|
|
188
|
+
break;
|
|
189
|
+
case "DELETE" /* DELETE */:
|
|
190
|
+
handled = await this.crudHandler.handleDelete?.(
|
|
191
|
+
entry,
|
|
192
|
+
this.supabase,
|
|
193
|
+
schema
|
|
194
|
+
) ?? false;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
if (handled) {
|
|
198
|
+
this.logger?.debug(
|
|
199
|
+
`[Connector] Custom handler processed ${entry.op} for ${schema}.${table}`
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const query = schema === "public" ? this.supabase.from(table) : this.supabase.schema(schema).from(table);
|
|
205
|
+
switch (entry.op) {
|
|
206
|
+
case "PUT" /* PUT */:
|
|
207
|
+
if (__DEV__) {
|
|
208
|
+
console.log("[Connector] Executing PUT/UPSERT:", {
|
|
209
|
+
schema,
|
|
210
|
+
table,
|
|
211
|
+
id,
|
|
212
|
+
data: { id, ...entry.opData }
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
const { data: upsertData, error: upsertError } = await query.upsert(
|
|
216
|
+
{ id, ...entry.opData },
|
|
217
|
+
{ onConflict: "id" }
|
|
218
|
+
).select();
|
|
219
|
+
if (upsertError) {
|
|
220
|
+
if (__DEV__) {
|
|
221
|
+
console.error("[Connector] PUT/UPSERT FAILED:", {
|
|
222
|
+
schema,
|
|
223
|
+
table,
|
|
224
|
+
id,
|
|
225
|
+
error: upsertError,
|
|
226
|
+
errorMessage: upsertError.message,
|
|
227
|
+
errorCode: upsertError.code,
|
|
228
|
+
errorDetails: upsertError.details,
|
|
229
|
+
errorHint: upsertError.hint
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Upsert failed for ${schema}.${table}: ${upsertError.message}`
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (__DEV__) {
|
|
237
|
+
console.log("[Connector] PUT/UPSERT SUCCESS:", {
|
|
238
|
+
schema,
|
|
239
|
+
table,
|
|
240
|
+
id,
|
|
241
|
+
responseData: upsertData
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
case "PATCH" /* PATCH */:
|
|
246
|
+
if (__DEV__) {
|
|
247
|
+
console.log("[Connector] Executing PATCH/UPDATE:", {
|
|
248
|
+
schema,
|
|
249
|
+
table,
|
|
250
|
+
id,
|
|
251
|
+
opData: entry.opData
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
const { data: updateData, error: updateError } = await query.update(entry.opData).eq("id", id).select();
|
|
255
|
+
if (updateError) {
|
|
256
|
+
if (__DEV__) {
|
|
257
|
+
console.error("[Connector] PATCH/UPDATE FAILED:", {
|
|
258
|
+
schema,
|
|
259
|
+
table,
|
|
260
|
+
id,
|
|
261
|
+
error: updateError,
|
|
262
|
+
errorMessage: updateError.message,
|
|
263
|
+
errorCode: updateError.code,
|
|
264
|
+
errorDetails: updateError.details,
|
|
265
|
+
errorHint: updateError.hint
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Update failed for ${schema}.${table}: ${updateError.message}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
if (__DEV__) {
|
|
273
|
+
console.log("[Connector] PATCH/UPDATE SUCCESS:", {
|
|
274
|
+
schema,
|
|
275
|
+
table,
|
|
276
|
+
id,
|
|
277
|
+
responseData: updateData
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
case "DELETE" /* DELETE */:
|
|
282
|
+
if (__DEV__) {
|
|
283
|
+
console.log("[Connector] Executing DELETE:", {
|
|
284
|
+
schema,
|
|
285
|
+
table,
|
|
286
|
+
id
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const { data: deleteData, error: deleteError } = await query.delete().eq("id", id).select();
|
|
290
|
+
if (deleteError) {
|
|
291
|
+
if (__DEV__) {
|
|
292
|
+
console.error("[Connector] DELETE FAILED:", {
|
|
293
|
+
schema,
|
|
294
|
+
table,
|
|
295
|
+
id,
|
|
296
|
+
error: deleteError,
|
|
297
|
+
errorMessage: deleteError.message,
|
|
298
|
+
errorCode: deleteError.code,
|
|
299
|
+
errorDetails: deleteError.details,
|
|
300
|
+
errorHint: deleteError.hint
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
throw new Error(
|
|
304
|
+
`Delete failed for ${schema}.${table}: ${deleteError.message}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (__DEV__) {
|
|
308
|
+
console.log("[Connector] DELETE SUCCESS:", {
|
|
309
|
+
schema,
|
|
310
|
+
table,
|
|
311
|
+
id,
|
|
312
|
+
responseData: deleteData
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
this.logger?.debug(
|
|
318
|
+
`[Connector] Processed ${entry.op} for ${schema}.${table} (id: ${id})`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export {
|
|
324
|
+
defaultSchemaRouter,
|
|
325
|
+
SupabaseConnector
|
|
326
|
+
};
|
|
327
|
+
//# sourceMappingURL=chunk-FLHDT4TS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connector/types.ts","../src/connector/supabase-connector.ts"],"sourcesContent":["/**\n * Connector Types for @pol-studios/powersync\n *\n * Defines interfaces for PowerSync backend connectors.\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase, ClassifiedError, CrudEntry } from '../core/types';\nimport type { LoggerAdapter } from '../platform/types';\n\n// ─── Connector Configuration ─────────────────────────────────────────────────\n\n/**\n * Options for creating a SupabaseConnector\n */\nexport interface SupabaseConnectorOptions {\n /** Supabase client instance */\n supabaseClient: SupabaseClient;\n /** PowerSync service URL */\n powerSyncUrl: string;\n /**\n * Optional: Custom schema routing function.\n * Determines which Supabase schema a table belongs to.\n * @default Returns 'public' for all tables\n */\n schemaRouter?: SchemaRouter;\n /**\n * Optional: Custom CRUD handler for complex mutations.\n * Allows overriding default upsert/update/delete behavior.\n */\n crudHandler?: CrudHandler;\n /** Logger for debugging */\n logger?: LoggerAdapter;\n /** Called when a transaction is successfully uploaded */\n onTransactionSuccess?: (entries: CrudEntry[]) => void;\n /** Called when a transaction fails to upload */\n onTransactionFailure?: (\n entries: CrudEntry[],\n error: Error,\n classified: ClassifiedError\n ) => void;\n /** Called when a transaction is fully completed (after transaction.complete()) */\n onTransactionComplete?: (entries: CrudEntry[]) => void;\n /** Function to check if upload should proceed. Used for sync mode gating. */\n shouldUpload?: () => boolean;\n}\n\n/**\n * Configuration passed to PowerSyncProvider for connector setup\n */\nexport interface ConnectorConfig {\n /**\n * Custom schema routing function.\n * Determines which Supabase schema a table belongs to.\n * @default Returns 'public' for all tables\n *\n * @example\n * ```typescript\n * schemaRouter: (table) => {\n * if (['Profile', 'Comment'].includes(table)) return 'core';\n * return 'public';\n * }\n * ```\n */\n schemaRouter?: SchemaRouter;\n\n /**\n * Custom CRUD handler for complex mutations.\n * @default Uses standard upsert/update/delete operations\n */\n crudHandler?: CrudHandler;\n\n /**\n * Token refresh configuration.\n */\n tokenRefresh?: {\n /** Refresh token when it expires within this many seconds (default: 60) */\n refreshThresholdSeconds?: number;\n };\n}\n\n// ─── Schema Routing ──────────────────────────────────────────────────────────\n\n/**\n * Function that determines which Supabase schema a table belongs to.\n *\n * @param tableName - The name of the table\n * @returns The schema name (e.g., 'public', 'core')\n */\nexport type SchemaRouter = (tableName: string) => string;\n\n/**\n * Default schema router that returns 'public' for all tables\n */\nexport const defaultSchemaRouter: SchemaRouter = () => 'public';\n\n// ─── CRUD Handling ───────────────────────────────────────────────────────────\n\n/**\n * Custom handler for CRUD operations.\n *\n * Return `true` from a handler to indicate the operation was handled.\n * Return `false` to fall back to default behavior.\n */\nexport interface CrudHandler {\n /**\n * Handle a PUT operation (insert or replace).\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handlePut?(\n entry: CrudEntry,\n supabase: SupabaseClient,\n schema: string\n ): Promise<boolean>;\n\n /**\n * Handle a PATCH operation (update).\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handlePatch?(\n entry: CrudEntry,\n supabase: SupabaseClient,\n schema: string\n ): Promise<boolean>;\n\n /**\n * Handle a DELETE operation.\n * @param entry - The CRUD entry\n * @param supabase - Supabase client\n * @param schema - The resolved schema for this table\n * @returns true if handled, false to use default behavior\n */\n handleDelete?(\n entry: CrudEntry,\n supabase: SupabaseClient,\n schema: string\n ): Promise<boolean>;\n}\n\n// ─── Credentials ─────────────────────────────────────────────────────────────\n\n/**\n * Credentials returned by fetchCredentials\n */\nexport interface PowerSyncCredentials {\n /** PowerSync service endpoint URL */\n endpoint: string;\n /** JWT token for authentication */\n token: string;\n /** When the token expires */\n expiresAt?: Date;\n}\n\n// ─── Re-export from Core ─────────────────────────────────────────────────────\n\n// Re-export PowerSyncBackendConnector from core/types to maintain API compatibility\nexport type { PowerSyncBackendConnector } from '../core/types';\n","/**\n * Supabase Connector for PowerSync\n *\n * A generic, configurable connector that handles:\n * - Authentication with Supabase JWT tokens\n * - Uploading local changes back to Supabase\n * - Schema routing for multi-schema databases\n * - Custom CRUD handling for complex operations\n */\n\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { AbstractPowerSyncDatabase, ClassifiedError, CrudEntry } from '../core/types';\nimport type { LoggerAdapter } from '../platform/types';\nimport type {\n SupabaseConnectorOptions,\n PowerSyncBackendConnector,\n PowerSyncCredentials,\n SchemaRouter,\n CrudHandler,\n} from './types';\nimport { defaultSchemaRouter } from './types';\nimport { classifySupabaseError } from '../core/errors';\n\n/**\n * Update type enum matching @powersync/common\n */\nenum UpdateType {\n PUT = 'PUT',\n PATCH = 'PATCH',\n DELETE = 'DELETE',\n}\n\n/**\n * Generic Supabase connector for PowerSync.\n *\n * This connector handles authentication and CRUD uploads to Supabase.\n * It supports configurable schema routing and custom CRUD handlers\n * for complex use cases.\n *\n * @example Basic usage\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * });\n * ```\n *\n * @example With schema routing\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * schemaRouter: (table) => {\n * if (['Profile', 'Comment', 'CommentSection'].includes(table)) {\n * return 'core';\n * }\n * return 'public';\n * },\n * });\n * ```\n *\n * @example With custom CRUD handler\n * ```typescript\n * const connector = new SupabaseConnector({\n * supabaseClient: supabase,\n * powerSyncUrl: 'https://your-powersync-instance.com',\n * crudHandler: {\n * async handlePut(entry, supabase, schema) {\n * // Custom handling for specific tables\n * if (entry.table === 'SpecialTable') {\n * await myCustomUpsert(entry);\n * return true; // Handled\n * }\n * return false; // Use default\n * },\n * },\n * });\n * ```\n */\nexport class SupabaseConnector implements PowerSyncBackendConnector {\n private readonly supabase: SupabaseClient;\n private readonly powerSyncUrl: string;\n private readonly schemaRouter: SchemaRouter;\n private readonly crudHandler?: CrudHandler;\n private readonly logger?: LoggerAdapter;\n private readonly onTransactionSuccess?: (entries: CrudEntry[]) => void;\n private readonly onTransactionFailure?: (\n entries: CrudEntry[],\n error: Error,\n classified: ClassifiedError\n ) => void;\n private readonly onTransactionComplete?: (entries: CrudEntry[]) => void;\n private readonly shouldUploadFn?: () => boolean;\n\n // Active project IDs for scoped sync (optional feature)\n private activeProjectIds: string[] = [];\n\n constructor(options: SupabaseConnectorOptions) {\n this.supabase = options.supabaseClient;\n this.powerSyncUrl = options.powerSyncUrl;\n this.schemaRouter = options.schemaRouter ?? defaultSchemaRouter;\n this.crudHandler = options.crudHandler;\n this.logger = options.logger;\n this.onTransactionSuccess = options.onTransactionSuccess;\n this.onTransactionFailure = options.onTransactionFailure;\n this.onTransactionComplete = options.onTransactionComplete;\n this.shouldUploadFn = options.shouldUpload;\n }\n\n /**\n * Set the active project IDs for scoped sync.\n * Call this when user selects/opens projects.\n */\n setActiveProjectIds(projectIds: string[]): void {\n this.activeProjectIds = projectIds;\n }\n\n /**\n * Get the current active project IDs.\n */\n getActiveProjectIds(): string[] {\n return this.activeProjectIds;\n }\n\n /**\n * Get credentials for PowerSync connection.\n * Uses Supabase session token.\n *\n * Note: Token refresh is handled by Supabase's startAutoRefresh() which must be\n * called on app initialization. getSession() returns the auto-refreshed token.\n */\n async fetchCredentials(): Promise<PowerSyncCredentials> {\n this.logger?.debug('[Connector] Fetching credentials...');\n\n const {\n data: { session },\n error,\n } = await this.supabase.auth.getSession();\n\n if (error) {\n this.logger?.error('[Connector] Auth error:', error);\n throw new Error(`Failed to get Supabase session: ${error.message}`);\n }\n\n if (!session) {\n this.logger?.error('[Connector] No active session');\n throw new Error('No active Supabase session');\n }\n\n this.logger?.debug(\n '[Connector] Credentials fetched, token expires at:',\n session.expires_at\n );\n\n return {\n endpoint: this.powerSyncUrl,\n token: session.access_token,\n expiresAt: session.expires_at\n ? new Date(session.expires_at * 1000)\n : undefined,\n };\n }\n\n /**\n * Upload local changes to Supabase.\n * Called automatically by PowerSync when there are pending uploads.\n */\n async uploadData(database: AbstractPowerSyncDatabase): Promise<void> {\n // Check if uploads are allowed based on sync mode\n if (this.shouldUploadFn && !this.shouldUploadFn()) {\n if (__DEV__) {\n console.log('[Connector] Upload skipped - sync mode does not allow uploads');\n }\n this.logger?.debug('[Connector] Upload skipped - sync mode does not allow uploads');\n return;\n }\n\n if (__DEV__) {\n console.log('[Connector] uploadData called, fetching next CRUD transaction...');\n }\n\n const transaction = await database.getNextCrudTransaction();\n\n if (!transaction) {\n if (__DEV__) {\n console.log('[Connector] No pending CRUD transaction found');\n }\n return;\n }\n\n if (__DEV__) {\n console.log('[Connector] Transaction fetched:', {\n crudCount: transaction.crud.length,\n entries: transaction.crud.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id,\n opDataKeys: e.opData ? Object.keys(e.opData) : [],\n })),\n });\n }\n\n try {\n for (const entry of transaction.crud) {\n if (__DEV__) {\n console.log('[Connector] Processing CRUD entry:', {\n table: entry.table,\n op: entry.op,\n id: entry.id,\n opData: entry.opData,\n });\n }\n await this.processCrudEntry(entry);\n }\n\n if (__DEV__) {\n console.log('[Connector] All CRUD entries processed, completing transaction...');\n }\n\n await transaction.complete();\n\n if (__DEV__) {\n console.log('[Connector] Transaction completed successfully:', {\n entriesCount: transaction.crud.length,\n });\n }\n\n // Notify success\n this.onTransactionSuccess?.(transaction.crud);\n\n // Notify completion (after transaction.complete() succeeds)\n this.onTransactionComplete?.(transaction.crud);\n } catch (error) {\n const classified = classifySupabaseError(error);\n\n // Always log to console for debugging\n console.error('[PowerSync Connector] Upload FAILED:', {\n errorMessage: error instanceof Error ? error.message : String(error),\n errorKeys: error && typeof error === 'object' ? Object.keys(error) : [],\n errorObject: JSON.stringify(error, null, 2), // Full structure\n classified,\n isPermanent: classified.isPermanent, // Explicitly log this\n entries: transaction.crud.map(e => ({\n table: e.table,\n op: e.op,\n id: e.id,\n })),\n });\n\n this.logger?.error('[Connector] Upload error:', {\n error,\n classified,\n entries: transaction.crud.map(e => ({ table: e.table, op: e.op, id: e.id })),\n });\n\n // Notify failure with classification\n this.onTransactionFailure?.(\n transaction.crud,\n error instanceof Error ? error : new Error(String(error)),\n classified\n );\n\n // Re-throw for PowerSync's native retry mechanism\n throw error;\n }\n }\n\n /**\n * Process a single CRUD operation.\n *\n * UUID-native tables (public schema, post-migration) use `id` as the UUID column.\n * Core schema tables (Profile, Comment, CommentSection) still use a separate `uuid` column.\n */\n /**\n * Process a single CRUD operation.\n *\n * All synced tables use `id` as their UUID primary key column.\n */\n private async processCrudEntry(entry: CrudEntry): Promise<void> {\n const table = entry.table;\n const id = entry.id; // PowerSync sends UUID as the entry.id\n const schema = this.schemaRouter(table);\n\n // Try custom handler first\n if (this.crudHandler) {\n let handled = false;\n\n switch (entry.op) {\n case UpdateType.PUT:\n handled =\n (await this.crudHandler.handlePut?.(entry, this.supabase, schema)) ??\n false;\n break;\n case UpdateType.PATCH:\n handled =\n (await this.crudHandler.handlePatch?.(\n entry,\n this.supabase,\n schema\n )) ?? false;\n break;\n case UpdateType.DELETE:\n handled =\n (await this.crudHandler.handleDelete?.(\n entry,\n this.supabase,\n schema\n )) ?? false;\n break;\n }\n\n if (handled) {\n this.logger?.debug(\n `[Connector] Custom handler processed ${entry.op} for ${schema}.${table}`\n );\n return;\n }\n }\n\n // Default behavior\n // Get the correct Supabase query builder for this table's schema\n const query =\n schema === 'public'\n ? this.supabase.from(table)\n : (this.supabase.schema(schema) as unknown as ReturnType<typeof this.supabase.schema>).from(table);\n\n switch (entry.op) {\n case UpdateType.PUT:\n if (__DEV__) {\n console.log('[Connector] Executing PUT/UPSERT:', {\n schema,\n table,\n id,\n data: { id, ...entry.opData },\n });\n }\n // Insert/upsert using id column\n const { data: upsertData, error: upsertError } = await query.upsert(\n { id, ...entry.opData },\n { onConflict: 'id' }\n ).select();\n\n if (upsertError) {\n if (__DEV__) {\n console.error('[Connector] PUT/UPSERT FAILED:', {\n schema,\n table,\n id,\n error: upsertError,\n errorMessage: upsertError.message,\n errorCode: upsertError.code,\n errorDetails: upsertError.details,\n errorHint: upsertError.hint,\n });\n }\n throw new Error(\n `Upsert failed for ${schema}.${table}: ${upsertError.message}`\n );\n }\n if (__DEV__) {\n console.log('[Connector] PUT/UPSERT SUCCESS:', {\n schema,\n table,\n id,\n responseData: upsertData,\n });\n }\n break;\n\n case UpdateType.PATCH:\n if (__DEV__) {\n console.log('[Connector] Executing PATCH/UPDATE:', {\n schema,\n table,\n id,\n opData: entry.opData,\n });\n }\n // Update by id column\n const { data: updateData, error: updateError } = await query\n .update(entry.opData)\n .eq('id', id)\n .select();\n\n if (updateError) {\n if (__DEV__) {\n console.error('[Connector] PATCH/UPDATE FAILED:', {\n schema,\n table,\n id,\n error: updateError,\n errorMessage: updateError.message,\n errorCode: updateError.code,\n errorDetails: updateError.details,\n errorHint: updateError.hint,\n });\n }\n throw new Error(\n `Update failed for ${schema}.${table}: ${updateError.message}`\n );\n }\n if (__DEV__) {\n console.log('[Connector] PATCH/UPDATE SUCCESS:', {\n schema,\n table,\n id,\n responseData: updateData,\n });\n }\n break;\n\n case UpdateType.DELETE:\n if (__DEV__) {\n console.log('[Connector] Executing DELETE:', {\n schema,\n table,\n id,\n });\n }\n // Delete by id column\n const { data: deleteData, error: deleteError } = await query.delete().eq('id', id).select();\n\n if (deleteError) {\n if (__DEV__) {\n console.error('[Connector] DELETE FAILED:', {\n schema,\n table,\n id,\n error: deleteError,\n errorMessage: deleteError.message,\n errorCode: deleteError.code,\n errorDetails: deleteError.details,\n errorHint: deleteError.hint,\n });\n }\n throw new Error(\n `Delete failed for ${schema}.${table}: ${deleteError.message}`\n );\n }\n if (__DEV__) {\n console.log('[Connector] DELETE SUCCESS:', {\n schema,\n table,\n id,\n responseData: deleteData,\n });\n }\n break;\n }\n\n this.logger?.debug(\n `[Connector] Processed ${entry.op} for ${schema}.${table} (id: ${id})`\n );\n }\n}\n"],"mappings":"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","names":[]}
|