@ohos-ports/libsql-client 0.18.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,268 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
28
+ var __importDefault = (this && this.__importDefault) || function (mod) {
29
+ return (mod && mod.__esModule) ? mod : { "default": mod };
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ exports.HttpTransaction = exports.HttpClient = exports._createClient = exports.createClient = void 0;
33
+ const hrana = __importStar(require("@libsql/hrana-client"));
34
+ const api_1 = require("@libsql/core/api");
35
+ const config_1 = require("@libsql/core/config");
36
+ const hrana_js_1 = require("./hrana.js");
37
+ const sql_cache_js_1 = require("./sql_cache.js");
38
+ const uri_1 = require("@libsql/core/uri");
39
+ const util_1 = require("@libsql/core/util");
40
+ const promise_limit_1 = __importDefault(require("promise-limit"));
41
+ __exportStar(require("@libsql/core/api"), exports);
42
+ function createClient(config) {
43
+ return _createClient((0, config_1.expandConfig)(config, true));
44
+ }
45
+ exports.createClient = createClient;
46
+ /** @private */
47
+ function _createClient(config) {
48
+ if (config.scheme !== "https" && config.scheme !== "http") {
49
+ throw new api_1.LibsqlError('The HTTP client supports only "libsql:", "https:" and "http:" URLs, ' +
50
+ `got ${JSON.stringify(config.scheme + ":")}. For more information, please read ${util_1.supportedUrlLink}`, "URL_SCHEME_NOT_SUPPORTED");
51
+ }
52
+ if (config.encryptionKey !== undefined) {
53
+ throw new api_1.LibsqlError("Encryption key is not supported by the remote client.", "ENCRYPTION_KEY_NOT_SUPPORTED");
54
+ }
55
+ if (config.scheme === "http" && config.tls) {
56
+ throw new api_1.LibsqlError(`A "http:" URL cannot opt into TLS by using ?tls=1`, "URL_INVALID");
57
+ }
58
+ else if (config.scheme === "https" && !config.tls) {
59
+ throw new api_1.LibsqlError(`A "https:" URL cannot opt out of TLS by using ?tls=0`, "URL_INVALID");
60
+ }
61
+ const url = (0, uri_1.encodeBaseUrl)(config.scheme, config.authority, config.path);
62
+ return new HttpClient(url, config.authToken, config.intMode, config.fetch, config.concurrency, config.remoteEncryptionKey);
63
+ }
64
+ exports._createClient = _createClient;
65
+ const sqlCacheCapacity = 30;
66
+ class HttpClient {
67
+ #client;
68
+ protocol;
69
+ #url;
70
+ #intMode;
71
+ #customFetch;
72
+ #concurrency;
73
+ #authToken;
74
+ #remoteEncryptionKey;
75
+ #promiseLimitFunction;
76
+ /** @private */
77
+ constructor(url, authToken, intMode, customFetch, concurrency, remoteEncryptionKey) {
78
+ this.#url = url;
79
+ this.#authToken = authToken;
80
+ this.#intMode = intMode;
81
+ this.#customFetch = customFetch;
82
+ this.#concurrency = concurrency;
83
+ this.#remoteEncryptionKey = remoteEncryptionKey;
84
+ this.#client = hrana.openHttp(this.#url, this.#authToken, this.#customFetch, remoteEncryptionKey);
85
+ this.#client.intMode = this.#intMode;
86
+ this.protocol = "http";
87
+ this.#promiseLimitFunction = (0, promise_limit_1.default)(this.#concurrency);
88
+ }
89
+ async limit(fn) {
90
+ return this.#promiseLimitFunction(fn);
91
+ }
92
+ async execute(stmtOrSql, args) {
93
+ let stmt;
94
+ if (typeof stmtOrSql === "string") {
95
+ stmt = {
96
+ sql: stmtOrSql,
97
+ args: args || [],
98
+ };
99
+ }
100
+ else {
101
+ stmt = stmtOrSql;
102
+ }
103
+ return this.limit(async () => {
104
+ try {
105
+ const hranaStmt = (0, hrana_js_1.stmtToHrana)(stmt);
106
+ // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the statement and
107
+ // close the stream in a single HTTP request.
108
+ let rowsPromise;
109
+ const stream = this.#client.openStream();
110
+ try {
111
+ rowsPromise = stream.query(hranaStmt);
112
+ }
113
+ finally {
114
+ stream.closeGracefully();
115
+ }
116
+ const rowsResult = await rowsPromise;
117
+ return (0, hrana_js_1.resultSetFromHrana)(rowsResult);
118
+ }
119
+ catch (e) {
120
+ throw (0, hrana_js_1.mapHranaError)(e);
121
+ }
122
+ });
123
+ }
124
+ async batch(stmts, mode = "deferred") {
125
+ return this.limit(async () => {
126
+ try {
127
+ const normalizedStmts = stmts.map((stmt) => {
128
+ if (Array.isArray(stmt)) {
129
+ return {
130
+ sql: stmt[0],
131
+ args: stmt[1] || [],
132
+ };
133
+ }
134
+ return stmt;
135
+ });
136
+ const hranaStmts = normalizedStmts.map(hrana_js_1.stmtToHrana);
137
+ const version = await this.#client.getVersion();
138
+ // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and
139
+ // close the stream in a single HTTP request.
140
+ let resultsPromise;
141
+ const stream = this.#client.openStream();
142
+ try {
143
+ // It makes sense to use a SQL cache even for a single batch, because it may contain the same
144
+ // statement repeated multiple times.
145
+ const sqlCache = new sql_cache_js_1.SqlCache(stream, sqlCacheCapacity);
146
+ sqlCache.apply(hranaStmts);
147
+ // TODO: we do not use a cursor here, because it would cause three roundtrips:
148
+ // 1. pipeline request to store SQL texts
149
+ // 2. cursor request
150
+ // 3. pipeline request to close the stream
151
+ const batch = stream.batch(false);
152
+ resultsPromise = (0, hrana_js_1.executeHranaBatch)(mode, version, batch, hranaStmts);
153
+ }
154
+ finally {
155
+ stream.closeGracefully();
156
+ }
157
+ const results = await resultsPromise;
158
+ return results;
159
+ }
160
+ catch (e) {
161
+ throw (0, hrana_js_1.mapHranaError)(e);
162
+ }
163
+ });
164
+ }
165
+ async migrate(stmts) {
166
+ return this.limit(async () => {
167
+ try {
168
+ const hranaStmts = stmts.map(hrana_js_1.stmtToHrana);
169
+ const version = await this.#client.getVersion();
170
+ // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the batch and
171
+ // close the stream in a single HTTP request.
172
+ let resultsPromise;
173
+ const stream = this.#client.openStream();
174
+ try {
175
+ const batch = stream.batch(false);
176
+ resultsPromise = (0, hrana_js_1.executeHranaBatch)("deferred", version, batch, hranaStmts, true);
177
+ }
178
+ finally {
179
+ stream.closeGracefully();
180
+ }
181
+ const results = await resultsPromise;
182
+ return results;
183
+ }
184
+ catch (e) {
185
+ throw (0, hrana_js_1.mapHranaError)(e);
186
+ }
187
+ });
188
+ }
189
+ async transaction(mode = "write") {
190
+ return this.limit(async () => {
191
+ try {
192
+ const version = await this.#client.getVersion();
193
+ return new HttpTransaction(this.#client.openStream(), mode, version);
194
+ }
195
+ catch (e) {
196
+ throw (0, hrana_js_1.mapHranaError)(e);
197
+ }
198
+ });
199
+ }
200
+ async executeMultiple(sql) {
201
+ return this.limit(async () => {
202
+ try {
203
+ // Pipeline all operations, so `hrana.HttpClient` can open the stream, execute the sequence and
204
+ // close the stream in a single HTTP request.
205
+ let promise;
206
+ const stream = this.#client.openStream();
207
+ try {
208
+ promise = stream.sequence(sql);
209
+ }
210
+ finally {
211
+ stream.closeGracefully();
212
+ }
213
+ await promise;
214
+ }
215
+ catch (e) {
216
+ throw (0, hrana_js_1.mapHranaError)(e);
217
+ }
218
+ });
219
+ }
220
+ sync() {
221
+ throw new api_1.LibsqlError("sync not supported in http mode", "SYNC_NOT_SUPPORTED");
222
+ }
223
+ close() {
224
+ this.#client.close();
225
+ }
226
+ async reconnect() {
227
+ try {
228
+ if (!this.closed) {
229
+ // Abort in-flight ops and free resources
230
+ this.#client.close();
231
+ }
232
+ }
233
+ finally {
234
+ // Recreate the underlying hrana client
235
+ this.#client = hrana.openHttp(this.#url, this.#authToken, this.#customFetch, this.#remoteEncryptionKey);
236
+ this.#client.intMode = this.#intMode;
237
+ }
238
+ }
239
+ get closed() {
240
+ return this.#client.closed;
241
+ }
242
+ }
243
+ exports.HttpClient = HttpClient;
244
+ class HttpTransaction extends hrana_js_1.HranaTransaction {
245
+ #stream;
246
+ #sqlCache;
247
+ /** @private */
248
+ constructor(stream, mode, version) {
249
+ super(mode, version);
250
+ this.#stream = stream;
251
+ this.#sqlCache = new sql_cache_js_1.SqlCache(stream, sqlCacheCapacity);
252
+ }
253
+ /** @private */
254
+ _getStream() {
255
+ return this.#stream;
256
+ }
257
+ /** @private */
258
+ _getSqlCache() {
259
+ return this.#sqlCache;
260
+ }
261
+ close() {
262
+ this.#stream.close();
263
+ }
264
+ get closed() {
265
+ return this.#stream.closed;
266
+ }
267
+ }
268
+ exports.HttpTransaction = HttpTransaction;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createClient = void 0;
18
+ const config_1 = require("@libsql/core/config");
19
+ const sqlite3_js_1 = require("./sqlite3.js");
20
+ const ws_js_1 = require("./ws.js");
21
+ const http_js_1 = require("./http.js");
22
+ __exportStar(require("@libsql/core/api"), exports);
23
+ /** Creates a {@link Client} object.
24
+ *
25
+ * You must pass at least an `url` in the {@link Config} object.
26
+ */
27
+ function createClient(config) {
28
+ return _createClient((0, config_1.expandConfig)(config, true));
29
+ }
30
+ exports.createClient = createClient;
31
+ function _createClient(config) {
32
+ if (config.scheme === "wss" || config.scheme === "ws") {
33
+ return (0, ws_js_1._createClient)(config);
34
+ }
35
+ else if (config.scheme === "https" || config.scheme === "http") {
36
+ return (0, http_js_1._createClient)(config);
37
+ }
38
+ else {
39
+ return (0, sqlite3_js_1._createClient)(config);
40
+ }
41
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SqlCache = void 0;
4
+ class SqlCache {
5
+ #owner;
6
+ #sqls;
7
+ capacity;
8
+ constructor(owner, capacity) {
9
+ this.#owner = owner;
10
+ this.#sqls = new Lru();
11
+ this.capacity = capacity;
12
+ }
13
+ // Replaces SQL strings with cached `hrana.Sql` objects in the statements in `hranaStmts`. After this
14
+ // function returns, we guarantee that all `hranaStmts` refer to valid (not closed) `hrana.Sql` objects,
15
+ // but _we may invalidate any other `hrana.Sql` objects_ (by closing them, thus removing them from the
16
+ // server).
17
+ //
18
+ // In practice, this means that after calling this function, you can use the statements only up to the
19
+ // first `await`, because concurrent code may also use the cache and invalidate those statements.
20
+ apply(hranaStmts) {
21
+ if (this.capacity <= 0) {
22
+ return;
23
+ }
24
+ const usedSqlObjs = new Set();
25
+ for (const hranaStmt of hranaStmts) {
26
+ if (typeof hranaStmt.sql !== "string") {
27
+ continue;
28
+ }
29
+ const sqlText = hranaStmt.sql;
30
+ // Stored SQL cannot exceed 5kb.
31
+ // https://github.com/tursodatabase/libsql/blob/e9d637e051685f92b0da43849507b5ef4232fbeb/libsql-server/src/hrana/http/request.rs#L10
32
+ if (sqlText.length >= 5000) {
33
+ continue;
34
+ }
35
+ let sqlObj = this.#sqls.get(sqlText);
36
+ if (sqlObj === undefined) {
37
+ while (this.#sqls.size + 1 > this.capacity) {
38
+ const [evictSqlText, evictSqlObj] = this.#sqls.peekLru();
39
+ if (usedSqlObjs.has(evictSqlObj)) {
40
+ // The SQL object that we are trying to evict is already in use in this batch, so we
41
+ // must not evict and close it.
42
+ break;
43
+ }
44
+ evictSqlObj.close();
45
+ this.#sqls.delete(evictSqlText);
46
+ }
47
+ if (this.#sqls.size + 1 <= this.capacity) {
48
+ sqlObj = this.#owner.storeSql(sqlText);
49
+ this.#sqls.set(sqlText, sqlObj);
50
+ }
51
+ }
52
+ if (sqlObj !== undefined) {
53
+ hranaStmt.sql = sqlObj;
54
+ usedSqlObjs.add(sqlObj);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ exports.SqlCache = SqlCache;
60
+ class Lru {
61
+ // This maps keys to the cache values. The entries are ordered by their last use (entires that were used
62
+ // most recently are at the end).
63
+ #cache;
64
+ constructor() {
65
+ this.#cache = new Map();
66
+ }
67
+ get(key) {
68
+ const value = this.#cache.get(key);
69
+ if (value !== undefined) {
70
+ // move the entry to the back of the Map
71
+ this.#cache.delete(key);
72
+ this.#cache.set(key, value);
73
+ }
74
+ return value;
75
+ }
76
+ set(key, value) {
77
+ this.#cache.set(key, value);
78
+ }
79
+ peekLru() {
80
+ for (const entry of this.#cache.entries()) {
81
+ return entry;
82
+ }
83
+ return undefined;
84
+ }
85
+ delete(key) {
86
+ this.#cache.delete(key);
87
+ }
88
+ get size() {
89
+ return this.#cache.size;
90
+ }
91
+ }