@capgo/capacitor-fast-sql 8.2.18 → 8.3.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/Package.swift +2 -1
- package/README.md +22 -14
- package/android/build.gradle +3 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/BinaryFrame.java +152 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/CapgoCapacitorFastSqlPlugin.java +5 -1
- package/android/src/main/java/app/capgo/capacitor/fastsql/DatabaseConnection.java +2 -0
- package/android/src/main/java/app/capgo/capacitor/fastsql/EncryptedSQLDatabase.java +13 -2
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLDatabase.java +13 -2
- package/android/src/main/java/app/capgo/capacitor/fastsql/SQLHTTPServer.java +114 -57
- package/dist/esm/binary-frame.d.ts +6 -0
- package/dist/esm/binary-frame.d.ts.map +1 -0
- package/dist/esm/binary-frame.js +100 -0
- package/dist/esm/definitions.d.ts +17 -0
- package/dist/esm/definitions.d.ts.map +1 -1
- package/dist/esm/fast-sql.d.ts.map +1 -1
- package/dist/esm/fast-sql.js +13 -3
- package/dist/esm/native-sql-connection.d.ts +18 -7
- package/dist/esm/native-sql-connection.d.ts.map +1 -1
- package/dist/esm/native-sql-connection.js +145 -59
- package/dist/esm/web.d.ts.map +1 -1
- package/dist/esm/web.js +2 -0
- package/dist/plugin.cjs.js +260 -62
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +260 -62
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/BinaryFrame.swift +95 -0
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/CapgoCapacitorFastSqlPlugin.swift +7 -3
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLDatabase.swift +20 -3
- package/ios/Sources/CapgoCapacitorFastSqlPlugin/SQLHTTPServer.swift +59 -76
- package/ios/Tests/CapgoCapacitorFastSqlPluginTests/BinaryFrameTests.swift +65 -0
- package/ios/Tests/CapgoCapacitorFastSqlPluginTests/Fixtures/binary-v1.bin +0 -0
- package/package.json +3 -2
|
@@ -3,15 +3,20 @@ package app.capgo.capacitor.fastsql;
|
|
|
3
3
|
import com.getcapacitor.JSArray;
|
|
4
4
|
import com.getcapacitor.JSObject;
|
|
5
5
|
import fi.iki.elonen.NanoHTTPD; // Note: org.nanohttpd:nanohttpd:2.3.1 still uses fi.iki.elonen package
|
|
6
|
+
import java.io.ByteArrayInputStream;
|
|
6
7
|
import java.io.DataInputStream;
|
|
7
8
|
import java.io.IOException;
|
|
8
9
|
import java.nio.charset.StandardCharsets;
|
|
9
10
|
import java.security.SecureRandom;
|
|
10
|
-
import java.util.List;
|
|
11
11
|
import java.util.Map;
|
|
12
|
+
import java.util.concurrent.ArrayBlockingQueue;
|
|
12
13
|
import java.util.concurrent.ExecutionException;
|
|
13
14
|
import java.util.concurrent.ExecutorService;
|
|
14
|
-
import java.util.concurrent.
|
|
15
|
+
import java.util.concurrent.Future;
|
|
16
|
+
import java.util.concurrent.RejectedExecutionException;
|
|
17
|
+
import java.util.concurrent.ThreadPoolExecutor;
|
|
18
|
+
import java.util.concurrent.TimeUnit;
|
|
19
|
+
import java.util.concurrent.TimeoutException;
|
|
15
20
|
import org.json.JSONArray;
|
|
16
21
|
import org.json.JSONObject;
|
|
17
22
|
|
|
@@ -25,12 +30,19 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
25
30
|
|
|
26
31
|
private final String token;
|
|
27
32
|
private final Map<String, DatabaseConnection> databases;
|
|
28
|
-
private final ExecutorService requestExecutor =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
private final ExecutorService requestExecutor = new ThreadPoolExecutor(
|
|
34
|
+
1,
|
|
35
|
+
1,
|
|
36
|
+
0,
|
|
37
|
+
TimeUnit.MILLISECONDS,
|
|
38
|
+
new ArrayBlockingQueue<>(32),
|
|
39
|
+
(runnable) -> {
|
|
40
|
+
Thread thread = new Thread(runnable);
|
|
41
|
+
thread.setName("FastSQL-DB");
|
|
42
|
+
thread.setDaemon(true);
|
|
43
|
+
return thread;
|
|
44
|
+
}
|
|
45
|
+
);
|
|
34
46
|
|
|
35
47
|
public SQLHTTPServer(Map<String, DatabaseConnection> databases) throws IOException {
|
|
36
48
|
super("127.0.0.1", 0);
|
|
@@ -54,6 +66,11 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
54
66
|
return response;
|
|
55
67
|
}
|
|
56
68
|
|
|
69
|
+
@Override
|
|
70
|
+
protected boolean useGzipWhenAccepted(Response response) {
|
|
71
|
+
return BinaryFrame.CONTENT_TYPE.equals(response.getMimeType()) || super.useGzipWhenAccepted(response);
|
|
72
|
+
}
|
|
73
|
+
|
|
57
74
|
@Override
|
|
58
75
|
public void stop() {
|
|
59
76
|
super.stop();
|
|
@@ -81,9 +98,25 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
81
98
|
return addCorsHeaders(newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "Database not found"));
|
|
82
99
|
}
|
|
83
100
|
|
|
101
|
+
Future<Response> request = null;
|
|
84
102
|
try {
|
|
85
|
-
|
|
103
|
+
request = requestExecutor.submit(() -> routeRequest(session, db));
|
|
104
|
+
return request.get(600, TimeUnit.SECONDS);
|
|
105
|
+
} catch (TimeoutException | RejectedExecutionException error) {
|
|
106
|
+
if (request != null) request.cancel(false);
|
|
107
|
+
Response response = newFixedLengthResponse(
|
|
108
|
+
Response.Status.SERVICE_UNAVAILABLE,
|
|
109
|
+
"text/plain",
|
|
110
|
+
"Request unavailable; a timed-out write may still complete. Do not automatically retry."
|
|
111
|
+
);
|
|
112
|
+
response.closeConnection(true);
|
|
113
|
+
return addCorsHeaders(response);
|
|
86
114
|
} catch (ExecutionException e) {
|
|
115
|
+
if (e.getCause() instanceof IOException || e.getCause() instanceof org.json.JSONException) {
|
|
116
|
+
Response response = newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", "Invalid SQL/frame request");
|
|
117
|
+
response.closeConnection(true);
|
|
118
|
+
return addCorsHeaders(response);
|
|
119
|
+
}
|
|
87
120
|
Throwable cause = e.getCause();
|
|
88
121
|
String message = cause != null ? cause.getMessage() : e.getMessage();
|
|
89
122
|
return addCorsHeaders(newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "text/plain", "Error: " + message));
|
|
@@ -97,10 +130,8 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
97
130
|
String uri = session.getUri();
|
|
98
131
|
Method method = session.getMethod();
|
|
99
132
|
|
|
100
|
-
if (method == Method.POST && uri.equals("/execute")) {
|
|
101
|
-
return addCorsHeaders(
|
|
102
|
-
} else if (method == Method.POST && uri.equals("/batch")) {
|
|
103
|
-
return addCorsHeaders(handleBatch(session, db));
|
|
133
|
+
if (method == Method.POST && (uri.equals("/execute") || uri.equals("/batch") || uri.equals("/echo"))) {
|
|
134
|
+
return addCorsHeaders(handleDataRequest(session, db));
|
|
104
135
|
} else if (method == Method.POST && uri.equals("/transaction/begin")) {
|
|
105
136
|
return addCorsHeaders(handleBeginTransaction(session, db));
|
|
106
137
|
} else if (method == Method.POST && uri.equals("/transaction/commit")) {
|
|
@@ -112,50 +143,64 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
112
143
|
}
|
|
113
144
|
}
|
|
114
145
|
|
|
115
|
-
private Response
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
private Response handleDataRequest(IHTTPSession session, DatabaseConnection db) throws Exception {
|
|
147
|
+
boolean binary = BinaryFrame.CONTENT_TYPE.equals(session.getHeaders().get("content-type"));
|
|
148
|
+
byte[] bytes = readRequestBytes(session);
|
|
149
|
+
Object decoded = binary ? BinaryFrame.decode(bytes) : new JSONObject(new String(bytes, StandardCharsets.UTF_8));
|
|
150
|
+
if (!(decoded instanceof JSONObject)) throw new IOException("Expected SQL request object");
|
|
151
|
+
JSONObject request = (JSONObject) decoded;
|
|
152
|
+
Object result;
|
|
153
|
+
if (session.getUri().equals("/batch")) {
|
|
154
|
+
JSONArray operations = request.getJSONArray("operations");
|
|
155
|
+
if (operations.length() > 10000) throw new IOException("Too many batch operations");
|
|
156
|
+
for (int i = 0; i < operations.length(); i++) validateOperation(operations.getJSONObject(i));
|
|
157
|
+
JSONArray results = new JSONArray();
|
|
158
|
+
for (int i = 0; i < operations.length(); i++) results.put(executeOperation(db, operations.getJSONObject(i), binary));
|
|
159
|
+
result = results;
|
|
160
|
+
} else {
|
|
161
|
+
validateOperation(request);
|
|
162
|
+
if (session.getUri().equals("/echo")) {
|
|
163
|
+
JSObject row = new JSObject();
|
|
164
|
+
if (request.getJSONArray("params").length() != 1) throw new IOException("Echo requires one parameter");
|
|
165
|
+
row.put("value", request.getJSONArray("params").get(0));
|
|
166
|
+
JSObject echo = new JSObject();
|
|
167
|
+
echo.put("rows", new JSONArray().put(row));
|
|
168
|
+
echo.put("rowsAffected", 0);
|
|
169
|
+
result = echo;
|
|
170
|
+
} else result = executeOperation(db, request, binary);
|
|
171
|
+
}
|
|
172
|
+
if (!binary) return newFixedLengthResponse(Response.Status.OK, "application/json", result.toString());
|
|
173
|
+
byte[] response;
|
|
174
|
+
try {
|
|
175
|
+
response = BinaryFrame.encode(result);
|
|
176
|
+
} catch (Exception error) {
|
|
177
|
+
throw new IllegalStateException("Could not encode SQL response; a write may already be committed", error);
|
|
178
|
+
}
|
|
179
|
+
return newFixedLengthResponse(Response.Status.OK, BinaryFrame.CONTENT_TYPE, new ByteArrayInputStream(response), response.length);
|
|
133
180
|
}
|
|
134
181
|
|
|
135
|
-
private
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
JSArray params = new JSArray();
|
|
150
|
-
String rawOperation = rawOperations.get(i);
|
|
151
|
-
String rawParamsArray = operation.has("params") ? HttpRequestJsonParams.extractJsonArrayAfterKey(rawOperation, "params") : null;
|
|
152
|
-
HttpRequestJsonParams.populateJsArrayFromParamsJson(paramsJson, params, rawParamsArray);
|
|
153
|
-
|
|
154
|
-
JSObject result = db.execute(statement, params);
|
|
155
|
-
results.put(result);
|
|
182
|
+
private static void validateOperation(JSONObject operation) throws Exception {
|
|
183
|
+
if (!(operation.get("statement") instanceof String)) throw new IOException("Expected SQL statement");
|
|
184
|
+
JSONArray params = operation.optJSONArray("params");
|
|
185
|
+
if (operation.has("params") && params == null) throw new IOException("Expected parameter array");
|
|
186
|
+
if (params == null) return;
|
|
187
|
+
for (int i = 0; i < params.length(); i++) {
|
|
188
|
+
Object value = params.get(i);
|
|
189
|
+
if (value == JSONObject.NULL || value instanceof String || value instanceof Boolean || value instanceof byte[]) continue;
|
|
190
|
+
if (value instanceof Number && Double.isFinite(((Number) value).doubleValue())) continue;
|
|
191
|
+
if (value instanceof JSONObject) {
|
|
192
|
+
JSONObject blob = (JSONObject) value;
|
|
193
|
+
if ("binary".equals(blob.optString("_type")) && blob.opt("_data") instanceof String) continue;
|
|
194
|
+
}
|
|
195
|
+
throw new IOException("Unsupported SQL parameter");
|
|
156
196
|
}
|
|
197
|
+
}
|
|
157
198
|
|
|
158
|
-
|
|
199
|
+
private static JSObject executeOperation(DatabaseConnection db, JSONObject operation, boolean binary) throws Exception {
|
|
200
|
+
JSONArray input = operation.optJSONArray("params");
|
|
201
|
+
JSArray params = new JSArray();
|
|
202
|
+
if (input != null) for (int i = 0; i < input.length(); i++) params.put(input.get(i));
|
|
203
|
+
return binary ? db.executeBinary(operation.getString("statement"), params) : db.execute(operation.getString("statement"), params);
|
|
159
204
|
}
|
|
160
205
|
|
|
161
206
|
private Response handleBeginTransaction(IHTTPSession session, DatabaseConnection db) throws Exception {
|
|
@@ -185,10 +230,22 @@ public class SQLHTTPServer extends NanoHTTPD {
|
|
|
185
230
|
}
|
|
186
231
|
|
|
187
232
|
private String readRequestBody(IHTTPSession session) throws IOException {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
233
|
+
return new String(readRequestBytes(session), StandardCharsets.UTF_8);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private byte[] readRequestBytes(IHTTPSession session) throws IOException {
|
|
237
|
+
if (session.getHeaders().containsKey("transfer-encoding")) throw new IOException("Chunked requests are unsupported");
|
|
238
|
+
String header = session.getHeaders().get("content-length");
|
|
239
|
+
long length;
|
|
240
|
+
try {
|
|
241
|
+
length = header == null ? 0 : Long.parseLong(header);
|
|
242
|
+
} catch (NumberFormatException error) {
|
|
243
|
+
throw new IOException("Invalid Content-Length", error);
|
|
244
|
+
}
|
|
245
|
+
if (length < 0 || length > BinaryFrame.MAX_BYTES) throw new IOException("Request exceeds 64 MiB");
|
|
246
|
+
byte[] bytes = new byte[(int) length];
|
|
247
|
+
new DataInputStream(session.getInputStream()).readFully(bytes);
|
|
248
|
+
return bytes;
|
|
192
249
|
}
|
|
193
250
|
|
|
194
251
|
private static String generateToken() {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Version 1 frame: FSQ1, uint32-LE metadata length, uint32-LE attachment length. */
|
|
2
|
+
export declare const BINARY_CONTENT_TYPE = "application/vnd.capgo.fast-sql.v1+binary";
|
|
3
|
+
export declare const MAX_FRAME_BYTES: number;
|
|
4
|
+
export declare function encodeBinaryFrame(value: unknown): Uint8Array<ArrayBuffer>;
|
|
5
|
+
export declare function decodeBinaryFrame(frame: Uint8Array): unknown;
|
|
6
|
+
//# sourceMappingURL=binary-frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-frame.d.ts","sourceRoot":"","sources":["../../src/binary-frame.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,6CAA6C,CAAC;AAC9E,eAAO,MAAM,eAAe,QAAmB,CAAC;AAMhD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAyCzE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAgD5D"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** Version 1 frame: FSQ1, uint32-LE metadata length, uint32-LE attachment length. */
|
|
2
|
+
export const BINARY_CONTENT_TYPE = 'application/vnd.capgo.fast-sql.v1+binary';
|
|
3
|
+
export const MAX_FRAME_BYTES = 64 * 1024 * 1024;
|
|
4
|
+
const MAX_METADATA_BYTES = 1024 * 1024;
|
|
5
|
+
const MAX_ATTACHMENTS = 4096;
|
|
6
|
+
const HEADER_BYTES = 12;
|
|
7
|
+
const magic = [70, 83, 81, 49];
|
|
8
|
+
export function encodeBinaryFrame(value) {
|
|
9
|
+
const attachments = [];
|
|
10
|
+
let size = 0;
|
|
11
|
+
let nodes = 0;
|
|
12
|
+
const encode = (item, depth) => {
|
|
13
|
+
if (++nodes > 100000 || depth > 32)
|
|
14
|
+
throw new Error('Binary metadata exceeds nesting or node limits');
|
|
15
|
+
if (typeof item === 'number' && !Number.isFinite(item))
|
|
16
|
+
throw new Error('Non-finite binary protocol number');
|
|
17
|
+
if (item instanceof Uint8Array) {
|
|
18
|
+
if (attachments.length >= MAX_ATTACHMENTS || size + item.byteLength > MAX_FRAME_BYTES - HEADER_BYTES)
|
|
19
|
+
throw new Error('Binary attachments exceed protocol limits');
|
|
20
|
+
const reference = { $blob: [size, item.byteLength] };
|
|
21
|
+
attachments.push(item);
|
|
22
|
+
size += item.byteLength;
|
|
23
|
+
return reference;
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(item))
|
|
26
|
+
return item.map((value) => encode(value, depth + 1));
|
|
27
|
+
if (item && typeof item === 'object') {
|
|
28
|
+
if (Object.getPrototypeOf(item) !== Object.prototype && Object.getPrototypeOf(item) !== null)
|
|
29
|
+
throw new Error('Unsupported binary metadata object');
|
|
30
|
+
return Object.fromEntries(Object.entries(item).map(([key, value]) => [key, encode(value, depth + 1)]));
|
|
31
|
+
}
|
|
32
|
+
return item;
|
|
33
|
+
};
|
|
34
|
+
const json = JSON.stringify(encode(value, 0));
|
|
35
|
+
if (json === undefined)
|
|
36
|
+
throw new Error('Missing binary metadata');
|
|
37
|
+
const metadata = new TextEncoder().encode(json);
|
|
38
|
+
const length = HEADER_BYTES + metadata.byteLength + size;
|
|
39
|
+
if (metadata.byteLength > MAX_METADATA_BYTES || length > MAX_FRAME_BYTES)
|
|
40
|
+
throw new Error('Binary frame exceeds protocol limits');
|
|
41
|
+
const frame = new Uint8Array(length);
|
|
42
|
+
frame.set(magic);
|
|
43
|
+
const view = new DataView(frame.buffer);
|
|
44
|
+
view.setUint32(4, metadata.byteLength, true);
|
|
45
|
+
view.setUint32(8, size, true);
|
|
46
|
+
frame.set(metadata, HEADER_BYTES);
|
|
47
|
+
let offset = HEADER_BYTES + metadata.byteLength;
|
|
48
|
+
for (const attachment of attachments) {
|
|
49
|
+
frame.set(attachment, offset);
|
|
50
|
+
offset += attachment.byteLength;
|
|
51
|
+
}
|
|
52
|
+
return frame;
|
|
53
|
+
}
|
|
54
|
+
export function decodeBinaryFrame(frame) {
|
|
55
|
+
if (frame.length < HEADER_BYTES ||
|
|
56
|
+
frame.length > MAX_FRAME_BYTES ||
|
|
57
|
+
magic.some((byte, index) => frame[index] !== byte)) {
|
|
58
|
+
throw new Error('Invalid binary frame version or length');
|
|
59
|
+
}
|
|
60
|
+
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
61
|
+
const metadataLength = view.getUint32(4, true);
|
|
62
|
+
const payloadLength = view.getUint32(8, true);
|
|
63
|
+
const payloadOffset = HEADER_BYTES + metadataLength;
|
|
64
|
+
if (metadataLength === 0 || metadataLength > MAX_METADATA_BYTES || payloadOffset + payloadLength !== frame.length) {
|
|
65
|
+
throw new Error('Invalid binary frame lengths');
|
|
66
|
+
}
|
|
67
|
+
const metadata = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(frame.subarray(HEADER_BYTES, payloadOffset)));
|
|
68
|
+
let nodes = 0;
|
|
69
|
+
let blobs = 0;
|
|
70
|
+
let referencedBytes = 0;
|
|
71
|
+
const decode = (value, depth) => {
|
|
72
|
+
if (++nodes > 100000 || depth > 32)
|
|
73
|
+
throw new Error('Binary metadata exceeds nesting or node limits');
|
|
74
|
+
if (Array.isArray(value))
|
|
75
|
+
return value.map((item) => decode(item, depth + 1));
|
|
76
|
+
if (value === null || typeof value !== 'object')
|
|
77
|
+
return value;
|
|
78
|
+
if ('$blob' in value && Object.keys(value).length === 1) {
|
|
79
|
+
const reference = value.$blob;
|
|
80
|
+
if (!Array.isArray(reference) || reference.length !== 2)
|
|
81
|
+
throw new Error('Invalid binary attachment reference');
|
|
82
|
+
const [offset, length] = reference;
|
|
83
|
+
if (typeof offset !== 'number' ||
|
|
84
|
+
typeof length !== 'number' ||
|
|
85
|
+
!Number.isSafeInteger(offset) ||
|
|
86
|
+
!Number.isSafeInteger(length) ||
|
|
87
|
+
offset < 0 ||
|
|
88
|
+
length < 0 ||
|
|
89
|
+
offset + length > payloadLength) {
|
|
90
|
+
throw new Error('Binary attachment is outside its frame');
|
|
91
|
+
}
|
|
92
|
+
referencedBytes += length;
|
|
93
|
+
if (++blobs > MAX_ATTACHMENTS || referencedBytes > MAX_FRAME_BYTES)
|
|
94
|
+
throw new Error('Decoded attachments exceed protocol limits');
|
|
95
|
+
return frame.slice(payloadOffset + offset, payloadOffset + offset + length);
|
|
96
|
+
}
|
|
97
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, decode(item, depth + 1)]));
|
|
98
|
+
};
|
|
99
|
+
return decode(metadata, 0);
|
|
100
|
+
}
|
|
@@ -38,10 +38,21 @@ export interface SQLBatchOperation {
|
|
|
38
38
|
*/
|
|
39
39
|
params?: SQLValue[];
|
|
40
40
|
}
|
|
41
|
+
/** Native HTTP data encoding. JSON remains the compatibility default. */
|
|
42
|
+
export type NativeSQLTransport = 'http-json' | 'http-binary';
|
|
41
43
|
/**
|
|
42
44
|
* Database connection options
|
|
43
45
|
*/
|
|
44
46
|
export interface SQLConnectionOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Native HTTP data encoding. Binary v1 sends BLOBs as raw bytes without base64.
|
|
49
|
+
* Opt-in on iOS/Android only; requires a native plugin advertising binary v1.
|
|
50
|
+
* JSON remains the default. Binary is experimental; reconsider the default only
|
|
51
|
+
* in a future major after device performance and conformance testing.
|
|
52
|
+
* No failed write is retried on another transport.
|
|
53
|
+
* @default 'http-json'
|
|
54
|
+
*/
|
|
55
|
+
transport?: NativeSQLTransport;
|
|
45
56
|
/**
|
|
46
57
|
* Database name (file will be created in app data directory)
|
|
47
58
|
*/
|
|
@@ -148,6 +159,8 @@ export interface CapgoCapacitorFastSqlPlugin {
|
|
|
148
159
|
database: string;
|
|
149
160
|
/** Whether the web Worker confirms persistent storage. Absent on native. */
|
|
150
161
|
persistent?: boolean;
|
|
162
|
+
/** Native binary HTTP protocol version, when supported. */
|
|
163
|
+
binaryProtocolVersion?: number;
|
|
151
164
|
}>;
|
|
152
165
|
/**
|
|
153
166
|
* Close database connection and stop the HTTP server.
|
|
@@ -184,6 +197,8 @@ export interface CapgoCapacitorFastSqlPlugin {
|
|
|
184
197
|
token: string;
|
|
185
198
|
/** Whether the web Worker confirms persistent storage. Absent on native. */
|
|
186
199
|
persistent?: boolean;
|
|
200
|
+
/** Native binary HTTP protocol version, when supported. */
|
|
201
|
+
binaryProtocolVersion?: number;
|
|
187
202
|
}>;
|
|
188
203
|
/**
|
|
189
204
|
* Execute a SQL query via Capacitor bridge (for simple queries).
|
|
@@ -276,6 +291,8 @@ export interface CapgoCapacitorFastSqlPlugin {
|
|
|
276
291
|
*/
|
|
277
292
|
getPluginVersion(): Promise<{
|
|
278
293
|
version: string;
|
|
294
|
+
/** Native transport capability, available without opening a database or starting a server. */
|
|
295
|
+
binaryProtocolVersion?: number;
|
|
279
296
|
}>;
|
|
280
297
|
/**
|
|
281
298
|
* Configure web-specific options for the official SQLite Wasm module.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;AACtD;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;;GAIG;AACH,oBAAY,cAAc;IACxB,eAAe,qBAAqB;IACpC,aAAa,mBAAmB;IAChC,cAAc,oBAAoB;IAClC,YAAY,iBAAiB;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,4EAA4E;QAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,IAAI,EAAE,MAAM,EAAE,CAAC;IAEf;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,aAAa,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAE/B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;AACtD;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;;GAIG;AACH,oBAAY,cAAc;IACxB,eAAe,qBAAqB;IACpC,aAAa,mBAAmB;IAChC,cAAc,oBAAoB;IAClC,YAAY,iBAAiB;CAC9B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;QAC9C,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,4EAA4E;QAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,2DAA2D;QAC3D,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IAEH;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,4EAA4E;QAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,2DAA2D;QAC3D,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IAEH;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAEnG;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhG;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE;;;;;;;;;;;;;;;;;OAiBG;IACH,mBAAmB,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElE;;;;;;;;;;;OAWG;IACH,gBAAgB,IAAI,OAAO,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC;QAChB,8FAA8F;QAC9F,qBAAqB,CAAC,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IAEH;;;;;;;;;;;;;;;;;OAiBG;IACH,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-sql.d.ts","sourceRoot":"","sources":["../../src/fast-sql.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAC,OAAO,CAA6C;IACnE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA2C;IACrE,OAAO,CAAC,MAAM,CAAC,WAAW,CAAyC;IACnE,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA0B;IAC1D,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAkC;IACpE,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAA0B;IAE3D;;;;;OAKG;WACU,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IA0B3E,OAAO,CAAC,MAAM,CAAC,YAAY;
|
|
1
|
+
{"version":3,"file":"fast-sql.d.ts","sourceRoot":"","sources":["../../src/fast-sql.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAC,OAAO,CAA6C;IACnE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA2C;IACrE,OAAO,CAAC,MAAM,CAAC,WAAW,CAAyC;IACnE,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAA0B;IAC1D,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAkC;IACpE,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAA0B;IAE3D;;;;;OAKG;WACU,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC;IA0B3E,OAAO,CAAC,MAAM,CAAC,YAAY;mBAWN,IAAI;IA0BzB;;;;OAIG;WACU,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBxD;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAI5D;;OAEG;IACH,MAAM,CAAC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIpD;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAO/C;;OAEG;WACU,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBlE;;OAEG;WACU,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3C;;;;OAIG;IACH,MAAM,CAAC,gBAAgB,IAAI,MAAM,EAAE;CAGpC"}
|
package/dist/esm/fast-sql.js
CHANGED
|
@@ -43,20 +43,30 @@ export class FastSQL {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
static optionsMatch(left, right) {
|
|
46
|
-
|
|
46
|
+
var _a, _b;
|
|
47
|
+
return (((_a = left.transport) !== null && _a !== void 0 ? _a : 'http-json') === ((_b = right.transport) !== null && _b !== void 0 ? _b : 'http-json') &&
|
|
48
|
+
Boolean(left.encrypted) === Boolean(right.encrypted) &&
|
|
47
49
|
left.encryptionKey === right.encryptionKey &&
|
|
48
50
|
Boolean(left.readOnly) === Boolean(right.readOnly) &&
|
|
49
51
|
Boolean(left.walMode) === Boolean(right.walMode) &&
|
|
50
52
|
Boolean(left.performancePresets) === Boolean(right.performancePresets));
|
|
51
53
|
}
|
|
52
54
|
static async open(options) {
|
|
55
|
+
const platform = Capacitor.getPlatform();
|
|
56
|
+
if (options.transport === 'http-binary' && platform !== 'ios' && platform !== 'android') {
|
|
57
|
+
throw new Error('Binary HTTP transport requires iOS or Android');
|
|
58
|
+
}
|
|
59
|
+
if (options.transport === 'http-binary') {
|
|
60
|
+
const capabilities = await CapgoCapacitorFastSql.getPluginVersion();
|
|
61
|
+
if (capabilities.binaryProtocolVersion !== 1)
|
|
62
|
+
throw new Error('Native plugin does not support binary HTTP v1');
|
|
63
|
+
}
|
|
53
64
|
// Connect via native plugin
|
|
54
65
|
const info = await CapgoCapacitorFastSql.connect(options);
|
|
55
66
|
// Create connection instance appropriate for the current platform
|
|
56
67
|
let connection;
|
|
57
|
-
const platform = Capacitor.getPlatform();
|
|
58
68
|
if (platform === 'android' || platform === 'ios') {
|
|
59
|
-
connection = new NativeSQLConnection(info.database, info.port, info.token);
|
|
69
|
+
connection = new NativeSQLConnection(info.database, info.port, info.token, options.transport);
|
|
60
70
|
}
|
|
61
71
|
else {
|
|
62
72
|
connection = new WebSQLConnection(info.database, CapgoCapacitorFastSql);
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import type { SQLValue, SQLRow, SQLResult, SQLBatchOperation } from './definitions';
|
|
1
|
+
import type { SQLValue, SQLRow, SQLResult, SQLBatchOperation, NativeSQLTransport } from './definitions';
|
|
2
2
|
import { IsolationLevel } from './definitions';
|
|
3
3
|
import type { SQLConnection } from './sql-connection';
|
|
4
|
+
export interface SQLTransportMetrics {
|
|
5
|
+
dataRequests: number;
|
|
6
|
+
requestBytes: number;
|
|
7
|
+
responseBytes: number;
|
|
8
|
+
encodeMs: number;
|
|
9
|
+
decodeMs: number;
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* SQL Connection class that uses HTTP protocol for efficient communication
|
|
6
13
|
* with native SQLite databases, bypassing Capacitor's standard bridge.
|
|
@@ -8,12 +15,16 @@ import type { SQLConnection } from './sql-connection';
|
|
|
8
15
|
* Inspired by capacitor-blob-writer's approach to avoid serialization overhead.
|
|
9
16
|
*/
|
|
10
17
|
export declare class NativeSQLConnection implements SQLConnection {
|
|
18
|
+
private readonly transport;
|
|
11
19
|
private port;
|
|
12
20
|
private token;
|
|
13
21
|
private database;
|
|
14
22
|
private baseUrl;
|
|
15
23
|
private inTransaction;
|
|
16
|
-
|
|
24
|
+
private metrics;
|
|
25
|
+
/** Cumulative data-request payload bytes and JS codec time; excludes HTTP headers/compression. */
|
|
26
|
+
getTransportMetrics(): SQLTransportMetrics;
|
|
27
|
+
constructor(database: string, port: number, token: string, transport?: NativeSQLTransport);
|
|
17
28
|
private browserFetch;
|
|
18
29
|
/**
|
|
19
30
|
* Get the database name
|
|
@@ -34,6 +45,11 @@ export declare class NativeSQLConnection implements SQLConnection {
|
|
|
34
45
|
* @returns Array of results for each operation
|
|
35
46
|
*/
|
|
36
47
|
executeBatch(operations: SQLBatchOperation[]): Promise<SQLResult[]>;
|
|
48
|
+
/** Authenticated transport round trip without SQLite, for diagnostic benchmarks. */
|
|
49
|
+
echo(value: SQLValue): Promise<SQLValue>;
|
|
50
|
+
private request;
|
|
51
|
+
private readResponse;
|
|
52
|
+
private parseResult;
|
|
37
53
|
/**
|
|
38
54
|
* Begin a transaction
|
|
39
55
|
*
|
|
@@ -79,11 +95,6 @@ export declare class NativeSQLConnection implements SQLConnection {
|
|
|
79
95
|
* Binary data (Uint8Array) is converted to base64 for JSON transport
|
|
80
96
|
*/
|
|
81
97
|
private serializeParams;
|
|
82
|
-
/**
|
|
83
|
-
* Deserialize result from server
|
|
84
|
-
* Base64-encoded binary data is converted back to Uint8Array
|
|
85
|
-
*/
|
|
86
|
-
private deserializeResult;
|
|
87
98
|
/**
|
|
88
99
|
* Convert Uint8Array to base64 string
|
|
89
100
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-sql-connection.d.ts","sourceRoot":"","sources":["../../src/native-sql-connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"native-sql-connection.d.ts","sourceRoot":"","sources":["../../src/native-sql-connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxG,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGtD,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IAuBrD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAtB5B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAMb;IAEF,kGAAkG;IAClG,mBAAmB,IAAI,mBAAmB;gBAKxC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACI,SAAS,GAAE,kBAAgC;IAQ9D,OAAO,CAAC,YAAY;IAMpB;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;;;;;OAMG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC;IAIzE;;;;;OAKG;IACG,YAAY,CAAC,UAAU,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQzE,oFAAoF;IAC9E,IAAI,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YAMhC,OAAO;YAwDP,YAAY;IAiC1B,OAAO,CAAC,WAAW;IA2CnB;;;;OAIG;IACG,gBAAgB,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BtE;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB7B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB/B;;;;;OAKG;IACG,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAYhH;;;;;;OAMG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKtE;;;;;;OAMG;IACG,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAQvG;;;OAGG;IACH,OAAO,CAAC,eAAe;IAYvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAQ1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;CAQ3B"}
|