@capgo/capacitor-fast-sql 7.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.
Files changed (37) hide show
  1. package/CapgoCapacitorFastSql.podspec +18 -0
  2. package/Package.swift +30 -0
  3. package/README.md +340 -0
  4. package/android/build.gradle +60 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/app/capgo/capacitor/fastsql/CapgoCapacitorFastSqlPlugin.java +220 -0
  7. package/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java +231 -0
  8. package/android/src/main/java/app/capgo/capacitor/fastsql/SQLHTTPServer.java +229 -0
  9. package/dist/esm/definitions.d.ts +225 -0
  10. package/dist/esm/definitions.d.ts.map +1 -0
  11. package/dist/esm/definitions.js +10 -0
  12. package/dist/esm/helpers.d.ts +7 -0
  13. package/dist/esm/helpers.d.ts.map +1 -0
  14. package/dist/esm/helpers.js +6 -0
  15. package/dist/esm/index.d.ts +5 -0
  16. package/dist/esm/index.d.ts.map +1 -0
  17. package/dist/esm/index.js +5 -0
  18. package/dist/esm/native-sql.d.ts +42 -0
  19. package/dist/esm/native-sql.d.ts.map +1 -0
  20. package/dist/esm/native-sql.js +69 -0
  21. package/dist/esm/plugin.d.ts +3 -0
  22. package/dist/esm/plugin.d.ts.map +1 -0
  23. package/dist/esm/plugin.js +4 -0
  24. package/dist/esm/sql-connection.d.ts +94 -0
  25. package/dist/esm/sql-connection.d.ts.map +1 -0
  26. package/dist/esm/sql-connection.js +246 -0
  27. package/dist/esm/web.d.ts +67 -0
  28. package/dist/esm/web.d.ts.map +1 -0
  29. package/dist/esm/web.js +215 -0
  30. package/dist/plugin.cjs.js +557 -0
  31. package/dist/plugin.cjs.js.map +1 -0
  32. package/dist/plugin.js +560 -0
  33. package/dist/plugin.js.map +1 -0
  34. package/ios/Sources/CapgoCapacitorFastSqlPlugin/CapgoCapacitorFastSqlPlugin.swift +202 -0
  35. package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift +215 -0
  36. package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLHTTPServer.swift +311 -0
  37. package/package.json +90 -0
@@ -0,0 +1,311 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import Telegraph
4
+
5
+ /**
6
+ * HTTP server for efficient SQL operations
7
+ *
8
+ * This server handles direct HTTP requests from JavaScript, bypassing Capacitor's
9
+ * bridge for better performance with large datasets and sync operations.
10
+ */
11
+ class SQLHTTPServer {
12
+ let port: Int
13
+ let token: String
14
+ private var databases: [String: SQLDatabase]
15
+ private var server: Server?
16
+ private var isRunning = false
17
+
18
+ init(databases: [String: SQLDatabase]) throws {
19
+ self.databases = databases
20
+ self.token = SQLHTTPServer.generateToken()
21
+ self.port = try SQLHTTPServer.findAvailablePort()
22
+
23
+ // Initialize Telegraph server
24
+ self.server = Server()
25
+ }
26
+
27
+ func start() throws {
28
+ guard !isRunning else { return }
29
+ guard let server = server else {
30
+ throw NSError(domain: "SQLHTTPServer", code: 1, userInfo: [
31
+ NSLocalizedDescriptionKey: "Server not initialized"
32
+ ])
33
+ }
34
+
35
+ // Configure routes
36
+ setupRoutes()
37
+
38
+ // Start server on localhost only
39
+ try server.start(port: port, interface: "127.0.0.1")
40
+ isRunning = true
41
+ }
42
+
43
+ func stop() {
44
+ server?.stop()
45
+ isRunning = false
46
+ }
47
+
48
+ // MARK: - Route Setup
49
+
50
+ private func setupRoutes() {
51
+ guard let server = server else { return }
52
+
53
+ // Execute endpoint
54
+ server.route(.POST, "/execute") { [weak self] request in
55
+ guard let self = self else {
56
+ return HTTPResponse(.internalServerError)
57
+ }
58
+ return self.handleExecuteRequest(request: request)
59
+ }
60
+
61
+ // Batch endpoint
62
+ server.route(.POST, "/batch") { [weak self] request in
63
+ guard let self = self else {
64
+ return HTTPResponse(.internalServerError)
65
+ }
66
+ return self.handleBatchRequest(request: request)
67
+ }
68
+
69
+ // Transaction endpoints
70
+ server.route(.POST, "/transaction/begin") { [weak self] request in
71
+ guard let self = self else {
72
+ return HTTPResponse(.internalServerError)
73
+ }
74
+ return self.handleBeginTransactionRequest(request: request)
75
+ }
76
+
77
+ server.route(.POST, "/transaction/commit") { [weak self] request in
78
+ guard let self = self else {
79
+ return HTTPResponse(.internalServerError)
80
+ }
81
+ return self.handleCommitTransactionRequest(request: request)
82
+ }
83
+
84
+ server.route(.POST, "/transaction/rollback") { [weak self] request in
85
+ guard let self = self else {
86
+ return HTTPResponse(.internalServerError)
87
+ }
88
+ return self.handleRollbackTransactionRequest(request: request)
89
+ }
90
+ }
91
+
92
+ // MARK: - Helper methods
93
+
94
+ private static func generateToken() -> String {
95
+ var bytes = [UInt8](repeating: 0, count: 32)
96
+ let result = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
97
+ guard result == errSecSuccess else {
98
+ return UUID().uuidString
99
+ }
100
+ return bytes.map { String(format: "%02hhx", $0) }.joined()
101
+ }
102
+
103
+ private static func findAvailablePort() throws -> Int {
104
+ // Try ports in the range 9000-9100
105
+ for port in 9000..<9100 {
106
+ let socket = socket(AF_INET, SOCK_STREAM, 0)
107
+ guard socket != -1 else {
108
+ continue
109
+ }
110
+
111
+ defer {
112
+ close(socket)
113
+ }
114
+
115
+ var addr = sockaddr_in()
116
+ addr.sin_family = sa_family_t(AF_INET)
117
+ addr.sin_port = UInt16(port).bigEndian
118
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1")
119
+
120
+ var bindAddr = addr
121
+ let bindResult = withUnsafePointer(to: &bindAddr) {
122
+ $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
123
+ bind(socket, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
124
+ }
125
+ }
126
+
127
+ if bindResult == 0 {
128
+ return port
129
+ }
130
+ }
131
+
132
+ // Fallback to 9000 if no port found
133
+ return 9000
134
+ }
135
+
136
+ // MARK: - Request Handlers
137
+
138
+ private func handleExecuteRequest(request: HTTPRequest) -> HTTPResponse {
139
+ // Authenticate
140
+ guard let authHeader = request.headers["Authorization"],
141
+ authHeader == "Bearer \(token)" else {
142
+ return HTTPResponse(.unauthorized)
143
+ }
144
+
145
+ // Get database name
146
+ guard let database = request.headers["X-Database"] else {
147
+ let bodyData = "Database header required".data(using: .utf8)!
148
+ return HTTPResponse(.badRequest, body: bodyData)
149
+ }
150
+
151
+ // Check if database exists
152
+ guard let db = databases[database] else {
153
+ let bodyData = "Database not found".data(using: .utf8)!
154
+ return HTTPResponse(.notFound, body: bodyData)
155
+ }
156
+
157
+ // Parse request body
158
+ let bodyData = request.body
159
+ guard let json = try? JSONSerialization.jsonObject(with: bodyData) as? [String: Any],
160
+ let statement = json["statement"] as? String else {
161
+ let errorData = "Invalid request body".data(using: .utf8)!
162
+ return HTTPResponse(.badRequest, body: errorData)
163
+ }
164
+
165
+ let params = (json["params"] as? [Any] ?? [])
166
+
167
+ do {
168
+ let result = try db.execute(statement: statement, params: params)
169
+ let resultData = try JSONSerialization.data(withJSONObject: result)
170
+ return HTTPResponse(.ok, headers: ["Content-Type": "application/json"], body: resultData)
171
+ } catch {
172
+ let errorData = "Error: \(error.localizedDescription)".data(using: .utf8)!
173
+ return HTTPResponse(.internalServerError, body: errorData)
174
+ }
175
+ }
176
+
177
+ private func handleBatchRequest(request: HTTPRequest) -> HTTPResponse {
178
+ // Authenticate
179
+ guard let authHeader = request.headers["Authorization"],
180
+ authHeader == "Bearer \(token)" else {
181
+ return HTTPResponse(.unauthorized)
182
+ }
183
+
184
+ // Get database name
185
+ guard let database = request.headers["X-Database"] else {
186
+ let bodyData = "Database header required".data(using: .utf8)!
187
+ return HTTPResponse(.badRequest, body: bodyData)
188
+ }
189
+
190
+ // Check if database exists
191
+ guard let db = databases[database] else {
192
+ let bodyData = "Database not found".data(using: .utf8)!
193
+ return HTTPResponse(.notFound, body: bodyData)
194
+ }
195
+
196
+ // Parse request body
197
+ let bodyData = request.body
198
+ guard let json = try? JSONSerialization.jsonObject(with: bodyData) as? [String: Any],
199
+ let operations = json["operations"] as? [[String: Any]] else {
200
+ let errorData = "Invalid request body".data(using: .utf8)!
201
+ return HTTPResponse(.badRequest, body: errorData)
202
+ }
203
+
204
+ var results: [[String: Any]] = []
205
+
206
+ do {
207
+ for operation in operations {
208
+ guard let statement = operation["statement"] as? String else {
209
+ let errorData = "Invalid operation: missing statement".data(using: .utf8)!
210
+ return HTTPResponse(.badRequest, body: errorData)
211
+ }
212
+ let params = (operation["params"] as? [Any] ?? [])
213
+ let result = try db.execute(statement: statement, params: params)
214
+ results.append(result)
215
+ }
216
+
217
+ let resultData = try JSONSerialization.data(withJSONObject: results)
218
+ return HTTPResponse(.ok, headers: ["Content-Type": "application/json"], body: resultData)
219
+ } catch {
220
+ let errorData = "Error: \(error.localizedDescription)".data(using: .utf8)!
221
+ return HTTPResponse(.internalServerError, body: errorData)
222
+ }
223
+ }
224
+
225
+ private func handleBeginTransactionRequest(request: HTTPRequest) -> HTTPResponse {
226
+ // Authenticate
227
+ guard let authHeader = request.headers["Authorization"],
228
+ authHeader == "Bearer \(token)" else {
229
+ return HTTPResponse(.unauthorized)
230
+ }
231
+
232
+ // Get database name
233
+ guard let database = request.headers["X-Database"] else {
234
+ let bodyData = "Database header required".data(using: .utf8)!
235
+ return HTTPResponse(.badRequest, body: bodyData)
236
+ }
237
+
238
+ // Check if database exists
239
+ guard let db = databases[database] else {
240
+ let bodyData = "Database not found".data(using: .utf8)!
241
+ return HTTPResponse(.notFound, body: bodyData)
242
+ }
243
+
244
+ do {
245
+ try db.beginTransaction()
246
+ let resultData = "{}".data(using: .utf8)!
247
+ return HTTPResponse(.ok, headers: ["Content-Type": "application/json"], body: resultData)
248
+ } catch {
249
+ let errorData = "Error: \(error.localizedDescription)".data(using: .utf8)!
250
+ return HTTPResponse(.internalServerError, body: errorData)
251
+ }
252
+ }
253
+
254
+ private func handleCommitTransactionRequest(request: HTTPRequest) -> HTTPResponse {
255
+ // Authenticate
256
+ guard let authHeader = request.headers["Authorization"],
257
+ authHeader == "Bearer \(token)" else {
258
+ return HTTPResponse(.unauthorized)
259
+ }
260
+
261
+ // Get database name
262
+ guard let database = request.headers["X-Database"] else {
263
+ let bodyData = "Database header required".data(using: .utf8)!
264
+ return HTTPResponse(.badRequest, body: bodyData)
265
+ }
266
+
267
+ // Check if database exists
268
+ guard let db = databases[database] else {
269
+ let bodyData = "Database not found".data(using: .utf8)!
270
+ return HTTPResponse(.notFound, body: bodyData)
271
+ }
272
+
273
+ do {
274
+ try db.commitTransaction()
275
+ let resultData = "{}".data(using: .utf8)!
276
+ return HTTPResponse(.ok, headers: ["Content-Type": "application/json"], body: resultData)
277
+ } catch {
278
+ let errorData = "Error: \(error.localizedDescription)".data(using: .utf8)!
279
+ return HTTPResponse(.internalServerError, body: errorData)
280
+ }
281
+ }
282
+
283
+ private func handleRollbackTransactionRequest(request: HTTPRequest) -> HTTPResponse {
284
+ // Authenticate
285
+ guard let authHeader = request.headers["Authorization"],
286
+ authHeader == "Bearer \(token)" else {
287
+ return HTTPResponse(.unauthorized)
288
+ }
289
+
290
+ // Get database name
291
+ guard let database = request.headers["X-Database"] else {
292
+ let bodyData = "Database header required".data(using: .utf8)!
293
+ return HTTPResponse(.badRequest, body: bodyData)
294
+ }
295
+
296
+ // Check if database exists
297
+ guard let db = databases[database] else {
298
+ let bodyData = "Database not found".data(using: .utf8)!
299
+ return HTTPResponse(.notFound, body: bodyData)
300
+ }
301
+
302
+ do {
303
+ try db.rollbackTransaction()
304
+ let resultData = "{}".data(using: .utf8)!
305
+ return HTTPResponse(.ok, headers: ["Content-Type": "application/json"], body: resultData)
306
+ } catch {
307
+ let errorData = "Error: \(error.localizedDescription)".data(using: .utf8)!
308
+ return HTTPResponse(.internalServerError, body: errorData)
309
+ }
310
+ }
311
+ }
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@capgo/capacitor-fast-sql",
3
+ "version": "7.0.1",
4
+ "description": "High-performance native SQLite plugin with custom protocol for efficient sync operations and IndexedDB replacement",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "CapgoCapacitorFastSql.podspec"
17
+ ],
18
+ "author": "Martin Donadieu <martin@capgo.app>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/Cap-go/capacitor-fast-sql.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/Cap-go/capacitor-fast-sql/issues"
26
+ },
27
+ "keywords": [
28
+ "capacitor",
29
+ "plugin",
30
+ "native",
31
+ "sqlite",
32
+ "sql",
33
+ "database",
34
+ "sync",
35
+ "indexeddb",
36
+ "performance"
37
+ ],
38
+ "scripts": {
39
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
40
+ "verify:ios": "xcodebuild -scheme CapgoCapacitorFastSql -destination generic/platform=iOS",
41
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
42
+ "verify:web": "npm run build",
43
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
44
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
45
+ "eslint": "eslint .",
46
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
47
+ "swiftlint": "node-swiftlint",
48
+ "docgen": "docgen --api CapgoCapacitorFastSqlPlugin --output-readme README.md --output-json dist/docs.json",
49
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
50
+ "clean": "rimraf ./dist",
51
+ "watch": "tsc --watch",
52
+ "prepublishOnly": "npm run build"
53
+ },
54
+ "devDependencies": {
55
+ "@capacitor/android": "^7.0.0",
56
+ "@capacitor/cli": "^7.0.0",
57
+ "@capacitor/core": "^7.0.0",
58
+ "@capacitor/docgen": "^0.3.0",
59
+ "@capacitor/ios": "^7.0.0",
60
+ "@ionic/eslint-config": "^0.4.0",
61
+ "@ionic/prettier-config": "^4.0.0",
62
+ "@ionic/swiftlint-config": "^2.0.0",
63
+ "@types/node": "^22.13.1",
64
+ "eslint": "^8.57.0",
65
+ "eslint-plugin-import": "^2.31.0",
66
+ "husky": "^9.1.7",
67
+ "prettier": "^3.4.2",
68
+ "prettier-plugin-java": "^2.6.7",
69
+ "rimraf": "^6.0.1",
70
+ "rollup": "^4.34.6",
71
+ "swiftlint": "^2.0.0",
72
+ "typescript": "^5.7.3"
73
+ },
74
+ "peerDependencies": {
75
+ "@capacitor/core": ">=7.0.0"
76
+ },
77
+ "eslintConfig": {
78
+ "extends": "@ionic/eslint-config/recommended"
79
+ },
80
+ "prettier": "@ionic/prettier-config",
81
+ "swiftlint": "@ionic/swiftlint-config",
82
+ "capacitor": {
83
+ "ios": {
84
+ "src": "ios"
85
+ },
86
+ "android": {
87
+ "src": "android"
88
+ }
89
+ }
90
+ }