@pol-studios/powersync 1.0.0 → 1.0.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.
- package/dist/chunk-42IJ25Q4.js +45 -0
- package/dist/chunk-42IJ25Q4.js.map +1 -0
- package/dist/{chunk-Q3LFFMRR.js → chunk-H7HZMI4H.js} +2 -2
- package/dist/chunk-MB2RC3NS.js +686 -0
- package/dist/chunk-MB2RC3NS.js.map +1 -0
- package/dist/{chunk-4FJVBR3X.js → chunk-PANEMMTU.js} +8 -3
- package/dist/chunk-PANEMMTU.js.map +1 -0
- package/dist/chunk-VJCL2SWD.js +1 -0
- package/dist/connector/index.d.ts +1 -2
- package/dist/connector/index.js +2 -5
- package/dist/{supabase-connector-D14-kl5v.d.ts → index-D952Qr38.d.ts} +152 -2
- package/dist/index.d.ts +2 -3
- package/dist/index.js +9 -9
- package/dist/index.native.d.ts +1 -2
- package/dist/index.native.js +10 -10
- package/dist/index.web.d.ts +1 -2
- package/dist/index.web.js +9 -9
- package/dist/platform/index.native.js +1 -1
- package/dist/provider/index.d.ts +1 -1
- package/dist/provider/index.js +2 -2
- package/package.json +33 -10
- package/dist/chunk-4FJVBR3X.js.map +0 -1
- package/dist/chunk-7BPTGEVG.js +0 -1
- package/dist/chunk-FLHDT4TS.js +0 -327
- package/dist/chunk-FLHDT4TS.js.map +0 -1
- package/dist/chunk-T225XEML.js +0 -298
- package/dist/chunk-T225XEML.js.map +0 -1
- package/dist/index-nae7nzib.d.ts +0 -147
- /package/dist/{chunk-Q3LFFMRR.js.map → chunk-H7HZMI4H.js.map} +0 -0
- /package/dist/{chunk-7BPTGEVG.js.map → chunk-VJCL2SWD.js.map} +0 -0
package/dist/provider/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
useSyncMode,
|
|
22
22
|
useSyncStatus,
|
|
23
23
|
useUploadStatus
|
|
24
|
-
} from "../chunk-
|
|
24
|
+
} from "../chunk-H7HZMI4H.js";
|
|
25
25
|
import "../chunk-GBGATW2S.js";
|
|
26
26
|
import {
|
|
27
27
|
DEFAULT_CONNECTION_HEALTH,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
DEFAULT_SYNC_STATUS
|
|
31
31
|
} from "../chunk-CFCK2LHI.js";
|
|
32
32
|
import "../chunk-NPNBGCRC.js";
|
|
33
|
-
import "../chunk-
|
|
33
|
+
import "../chunk-MB2RC3NS.js";
|
|
34
34
|
import "../chunk-CHRTN5PF.js";
|
|
35
35
|
export {
|
|
36
36
|
AttachmentQueueContext,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pol-studios/powersync",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Enterprise PowerSync integration for offline-first applications",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
"types": "./dist/sync/index.d.ts"
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
-
"files": [
|
|
59
|
+
"files": [
|
|
60
|
+
"dist"
|
|
61
|
+
],
|
|
60
62
|
"scripts": {
|
|
61
63
|
"build": "NODE_OPTIONS='--max-old-space-size=8192' tsup",
|
|
62
64
|
"dev": "tsup --watch",
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
},
|
|
66
68
|
"peerDependencies": {
|
|
67
69
|
"@powersync/react-native": ">=1.0.8",
|
|
70
|
+
"@powersync/op-sqlite": ">=0.8.0",
|
|
68
71
|
"@powersync/web": ">=1.0.0",
|
|
69
72
|
"@powersync/attachments": ">=2.0.0",
|
|
70
73
|
"@react-native-async-storage/async-storage": ">=1.0.0",
|
|
@@ -77,20 +80,40 @@
|
|
|
77
80
|
"react-native": ">=0.70.0"
|
|
78
81
|
},
|
|
79
82
|
"peerDependenciesMeta": {
|
|
80
|
-
"@powersync/react-native": {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"@
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"
|
|
87
|
-
|
|
83
|
+
"@powersync/react-native": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@powersync/op-sqlite": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@powersync/web": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@powersync/attachments": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"@react-native-async-storage/async-storage": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"@react-native-community/netinfo": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
101
|
+
"expo-file-system": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"expo-image-manipulator": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"react-native": {
|
|
108
|
+
"optional": true
|
|
109
|
+
}
|
|
88
110
|
},
|
|
89
111
|
"dependencies": {
|
|
90
112
|
"@pol-studios/db": "workspace:*"
|
|
91
113
|
},
|
|
92
114
|
"devDependencies": {
|
|
93
115
|
"@powersync/common": "^1.0.0",
|
|
116
|
+
"@powersync/op-sqlite": "^0.8.0",
|
|
94
117
|
"@types/react": "^18.0.0",
|
|
95
118
|
"tsup": "^8.0.0",
|
|
96
119
|
"typescript": "^5.0.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/platform/index.native.ts"],"sourcesContent":["/**\n * React Native Platform Adapter for @pol-studios/powersync\n *\n * Implements the PlatformAdapter interface using React Native specific APIs:\n * - @powersync/react-native for SQLite database\n * - expo-file-system for file operations\n * - @react-native-async-storage/async-storage for key-value storage\n * - @react-native-community/netinfo for network monitoring\n * - expo-image-manipulator for image compression\n */\n\nimport type {\n PlatformAdapter,\n DatabaseOptions,\n FileSystemAdapter,\n AsyncStorageAdapter,\n NetworkAdapter,\n LoggerAdapter,\n ImageProcessorAdapter,\n FileInfo,\n CompressedImage,\n CompressionOptions,\n ConnectionType,\n} from './types';\nimport type { AbstractPowerSyncDatabase } from '../core/types';\n\n/**\n * Create a React Native platform adapter\n *\n * @param logger - Logger implementation to use\n * @returns Platform adapter configured for React Native\n *\n * @example\n * ```typescript\n * import { createNativePlatformAdapter } from '@pol-studios/powersync/platform';\n *\n * const logger = {\n * debug: console.log,\n * info: console.log,\n * warn: console.warn,\n * error: console.error,\n * };\n *\n * const platform = createNativePlatformAdapter(logger);\n * ```\n */\nexport function createNativePlatformAdapter(\n logger: LoggerAdapter\n): PlatformAdapter {\n // Lazy imports to avoid loading these modules until needed\n // This also helps with tree-shaking in web builds\n let PowerSyncDatabase: typeof import('@powersync/react-native').PowerSyncDatabase;\n // Use 'any' for FileSystem to handle type changes between expo-file-system versions\n let FileSystem: any;\n let AsyncStorage: typeof import('@react-native-async-storage/async-storage').default;\n let NetInfo: typeof import('@react-native-community/netinfo').default;\n let ImageManipulator: typeof import('expo-image-manipulator');\n\n const loadDependencies = async () => {\n if (!PowerSyncDatabase) {\n const psModule = await import('@powersync/react-native');\n PowerSyncDatabase = psModule.PowerSyncDatabase;\n }\n if (!FileSystem) {\n FileSystem = await import('expo-file-system');\n }\n if (!AsyncStorage) {\n const asModule = await import('@react-native-async-storage/async-storage');\n AsyncStorage = asModule.default;\n }\n if (!NetInfo) {\n const niModule = await import('@react-native-community/netinfo');\n NetInfo = niModule.default;\n }\n if (!ImageManipulator) {\n ImageManipulator = await import('expo-image-manipulator');\n }\n };\n\n // File system adapter implementation\n const fileSystem: FileSystemAdapter = {\n async readFile(uri: string, encoding: 'base64' | 'utf8' = 'utf8'): Promise<string> {\n if (!FileSystem) await loadDependencies();\n return FileSystem!.readAsStringAsync(uri, {\n encoding:\n encoding === 'base64'\n ? FileSystem!.EncodingType.Base64\n : FileSystem!.EncodingType.UTF8,\n });\n },\n\n async writeFile(\n uri: string,\n data: string,\n encoding: 'base64' | 'utf8' = 'utf8'\n ): Promise<void> {\n if (!FileSystem) await loadDependencies();\n // Ensure parent directory exists\n const parentDir = uri.substring(0, uri.lastIndexOf('/'));\n if (parentDir) {\n await FileSystem!.makeDirectoryAsync(parentDir, { intermediates: true }).catch(\n () => {\n // Directory may already exist\n }\n );\n }\n await FileSystem!.writeAsStringAsync(uri, data, {\n encoding:\n encoding === 'base64'\n ? FileSystem!.EncodingType.Base64\n : FileSystem!.EncodingType.UTF8,\n });\n },\n\n async deleteFile(uri: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.deleteAsync(uri, { idempotent: true });\n },\n\n async copyFile(source: string, destination: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.copyAsync({ from: source, to: destination });\n },\n\n async moveFile(source: string, destination: string): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.moveAsync({ from: source, to: destination });\n },\n\n async getFileInfo(uri: string): Promise<FileInfo | null> {\n if (!FileSystem) await loadDependencies();\n try {\n const info = await FileSystem!.getInfoAsync(uri);\n if (!info.exists) return null;\n return {\n exists: true,\n size: 'size' in info ? (info.size ?? 0) : 0,\n isDirectory: info.isDirectory ?? false,\n modificationTime:\n 'modificationTime' in info && info.modificationTime\n ? new Date(info.modificationTime * 1000)\n : undefined,\n };\n } catch {\n return null;\n }\n },\n\n async makeDirectory(\n uri: string,\n options?: { intermediates?: boolean }\n ): Promise<void> {\n if (!FileSystem) await loadDependencies();\n await FileSystem!.makeDirectoryAsync(uri, {\n intermediates: options?.intermediates ?? true,\n });\n },\n\n getDocumentsDirectory(): string {\n // Note: This will be set after first load\n // Return a placeholder that will work for path construction\n return FileSystem?.documentDirectory ?? '';\n },\n\n getCacheDirectory(): string {\n return FileSystem?.cacheDirectory ?? '';\n },\n\n async getFreeDiskSpace(): Promise<number> {\n if (!FileSystem) await loadDependencies();\n return FileSystem!.getFreeDiskStorageAsync();\n },\n };\n\n // Async storage adapter implementation\n const storage: AsyncStorageAdapter = {\n async getItem(key: string): Promise<string | null> {\n if (!AsyncStorage) await loadDependencies();\n return AsyncStorage!.getItem(key);\n },\n\n async setItem(key: string, value: string): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.setItem(key, value);\n },\n\n async removeItem(key: string): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.removeItem(key);\n },\n\n async multiGet(keys: string[]): Promise<[string, string | null][]> {\n if (!AsyncStorage) await loadDependencies();\n const result = await AsyncStorage!.multiGet(keys);\n return result as [string, string | null][];\n },\n\n async multiSet(entries: [string, string][]): Promise<void> {\n if (!AsyncStorage) await loadDependencies();\n await AsyncStorage!.multiSet(entries);\n },\n };\n\n // Network adapter implementation\n const network: NetworkAdapter = {\n async isConnected(): Promise<boolean> {\n if (!NetInfo) await loadDependencies();\n const state = await NetInfo!.fetch();\n return state.isConnected ?? false;\n },\n\n async getConnectionType(): Promise<ConnectionType> {\n if (!NetInfo) await loadDependencies();\n const state = await NetInfo!.fetch();\n const type = state.type;\n if (type === 'wifi') return 'wifi';\n if (type === 'cellular') return 'cellular';\n if (type === 'ethernet') return 'ethernet';\n if (type === 'none') return 'none';\n return 'unknown';\n },\n\n addConnectionListener(callback: (isConnected: boolean) => void): () => void {\n // NetInfo must be loaded synchronously for listener setup\n // This is typically called after initialization\n if (!NetInfo) {\n logger.warn(\n '[Platform] NetInfo not loaded, connection listener may not work immediately'\n );\n // Load and set up listener asynchronously\n loadDependencies().then(() => {\n NetInfo!.addEventListener((state) => {\n callback(state.isConnected ?? false);\n });\n });\n return () => {};\n }\n\n const unsubscribe = NetInfo.addEventListener((state) => {\n callback(state.isConnected ?? false);\n });\n return unsubscribe;\n },\n };\n\n // Image processor adapter implementation\n const imageProcessor: ImageProcessorAdapter = {\n async compress(\n uri: string,\n options: CompressionOptions\n ): Promise<CompressedImage> {\n if (!ImageManipulator) await loadDependencies();\n\n const actions: import('expo-image-manipulator').Action[] = [];\n\n // Add resize action if maxWidth or maxHeight specified\n if (options.maxWidth || options.maxHeight) {\n actions.push({\n resize: {\n width: options.maxWidth,\n height: options.maxHeight,\n },\n });\n }\n\n // Determine output format\n let format: import('expo-image-manipulator').SaveFormat;\n switch (options.format) {\n case 'png':\n format = ImageManipulator!.SaveFormat.PNG;\n break;\n case 'webp':\n format = ImageManipulator!.SaveFormat.WEBP;\n break;\n case 'jpeg':\n default:\n format = ImageManipulator!.SaveFormat.JPEG;\n break;\n }\n\n const result = await ImageManipulator!.manipulateAsync(uri, actions, {\n compress: options.quality,\n format,\n });\n\n return {\n uri: result.uri,\n width: result.width,\n height: result.height,\n };\n },\n };\n\n // Main platform adapter\n return {\n async createDatabase(\n options: DatabaseOptions\n ): Promise<AbstractPowerSyncDatabase> {\n if (!PowerSyncDatabase) await loadDependencies();\n\n logger.info('[Platform] Creating PowerSync database:', options.dbFilename);\n\n const db = new PowerSyncDatabase!({\n schema: options.schema as any,\n database: {\n dbFilename: options.dbFilename,\n },\n });\n\n logger.info('[Platform] Initializing database...');\n await db.init();\n\n // Verify database is queryable before returning\n // This prevents race conditions where db.connect() is called before SQLite is truly ready\n const maxAttempts = 3;\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n try {\n await db.get('SELECT 1');\n logger.info('[Platform] Database initialized and verified');\n return db as unknown as AbstractPowerSyncDatabase;\n } catch (err) {\n if (attempt < maxAttempts - 1) {\n logger.warn(`[Platform] Database readiness check failed (attempt ${attempt + 1}/${maxAttempts}), retrying...`);\n await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt))); // 100ms, 200ms, 400ms\n } else {\n logger.error('[Platform] Database failed readiness verification after all attempts');\n throw new Error(`Database failed readiness verification: ${err instanceof Error ? err.message : err}`);\n }\n }\n }\n\n // TypeScript: unreachable but needed for return type\n throw new Error('Database readiness verification failed');\n },\n\n fileSystem,\n storage,\n network,\n logger,\n imageProcessor,\n };\n}\n\n// Re-export types for convenience\nexport type { PlatformAdapter, LoggerAdapter } from './types';\n"],"mappings":"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","names":[]}
|
package/dist/chunk-7BPTGEVG.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-7BPTGEVG.js.map
|
package/dist/chunk-FLHDT4TS.js
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|