From da2769cd524f5f351b2bf0f63dfcd1d715ecf7a3 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 14:59:16 +0530 Subject: [PATCH 1/3] Add support for propagating remote encryption key --- src/http/client.ts | 6 ++++-- src/http/stream.ts | 7 ++++++- src/index.ts | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/http/client.ts b/src/http/client.ts index 05db6ea..d8434c4 100644 --- a/src/http/client.ts +++ b/src/http/client.ts @@ -46,6 +46,7 @@ export class HttpClient extends Client { #url: URL; #jwt: string | undefined; #fetch: typeof fetch; + #remoteEncryptionKey: string | undefined; #closed: Error | undefined; #streams: Set; @@ -56,11 +57,12 @@ export class HttpClient extends Client { _endpoint: Endpoint | undefined; /** @private */ - constructor(url: URL, jwt: string | undefined, customFetch: unknown | undefined, protocolVersion: ProtocolVersion = 2) { + constructor(url: URL, jwt: string | undefined, customFetch: unknown | undefined, remoteEncryptionKey?: string, protocolVersion: ProtocolVersion = 2) { super(); this.#url = url; this.#jwt = jwt; this.#fetch = (customFetch as typeof fetch) ?? fetch; + this.#remoteEncryptionKey = remoteEncryptionKey; this.#closed = undefined; this.#streams = new Set(); @@ -112,7 +114,7 @@ export class HttpClient extends Client { if (this.#closed !== undefined) { throw new ClosedError("Client is closed", this.#closed); } - const stream = new HttpStream(this, this.#url, this.#jwt, this.#fetch); + const stream = new HttpStream(this, this.#url, this.#jwt, this.#fetch, this.#remoteEncryptionKey); this.#streams.add(stream); return stream; } diff --git a/src/http/stream.ts b/src/http/stream.ts index f201731..ea9a635 100644 --- a/src/http/stream.ts +++ b/src/http/stream.ts @@ -53,6 +53,7 @@ export class HttpStream extends Stream implements SqlOwner { #baseUrl: string; #jwt: string | undefined; #fetch: typeof fetch; + #remoteEncryptionKey: string | undefined; #baton: string | undefined; #queue: Queue; @@ -65,12 +66,13 @@ export class HttpStream extends Stream implements SqlOwner { #sqlIdAlloc: IdAlloc; /** @private */ - constructor(client: HttpClient, baseUrl: URL, jwt: string | undefined, customFetch: typeof fetch) { + constructor(client: HttpClient, baseUrl: URL, jwt: string | undefined, customFetch: typeof fetch, remoteEncryptionKey?: string) { super(client.intMode); this.#client = client; this.#baseUrl = baseUrl.toString(); this.#jwt = jwt; this.#fetch = customFetch; + this.#remoteEncryptionKey = remoteEncryptionKey; this.#baton = undefined; this.#queue = new Queue(); @@ -412,6 +414,9 @@ export class HttpStream extends Stream implements SqlOwner { if (this.#jwt !== undefined) { headers.set("authorization", `Bearer ${this.#jwt}`); } + if (this.#remoteEncryptionKey !== undefined) { + headers.set("x-turso-encryption-key", this.#remoteEncryptionKey); + } return new Request(url.toString(), {method: "POST", headers, body: bodyData}); } diff --git a/src/index.ts b/src/index.ts index 43d4052..490d5af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -52,6 +52,6 @@ export function openWs(url: string | URL, jwt?: string, protocolVersion: Protoco * from `cross-fetch`. This function is always called with a `Request` object from * `cross-fetch`. */ -export function openHttp(url: string | URL, jwt?: string, customFetch?: unknown | undefined, protocolVersion: ProtocolVersion = 2): HttpClient { - return new HttpClient(url instanceof URL ? url : new URL(url), jwt, customFetch, protocolVersion); +export function openHttp(url: string | URL, jwt?: string, customFetch?: unknown | undefined, remoteEncryptionKey?: string, protocolVersion: ProtocolVersion = 2): HttpClient { + return new HttpClient(url instanceof URL ? url : new URL(url), jwt, customFetch, remoteEncryptionKey, protocolVersion); } From 5b4aafc8eba766a1337c91bdd35a9e7d1d075297 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 15:00:17 +0530 Subject: [PATCH 2/3] version bump to 0.9.0 --- CHANGELOG.md | 4 ++++ package-lock.json | 8 ++++++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c701634..fdcfdff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.9.0 -- 2025-12-19 + +- Add support for propagating remote encryption key + ## 0.8.0 -- 2024-09-16 - Replace isomorphic-fetch dependency with cross-fetch diff --git a/package-lock.json b/package-lock.json index e7fe187..3cffe4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@libsql/hrana-client", - "version": "0.8.0", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@libsql/hrana-client", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "dependencies": { "@libsql/isomorphic-ws": "^0.1.5", @@ -133,6 +133,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.10", @@ -1370,6 +1371,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001517", "electron-to-chromium": "^1.4.477", @@ -2163,6 +2165,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^29.6.2", "@jest/types": "^29.6.1", @@ -3727,6 +3730,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 49fdada..cb783d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@libsql/hrana-client", - "version": "0.8.0", + "version": "0.9.0", "keywords": [ "hrana", "libsql", From 270708f3ff11e4271b76c683c1cb96cfcbeac713 Mon Sep 17 00:00:00 2001 From: Avinash Sajjanshetty Date: Fri, 19 Dec 2025 15:09:03 +0530 Subject: [PATCH 3/3] fix tests --- src/__tests__/client.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/__tests__/client.ts b/src/__tests__/client.ts index 47ae0a9..fb259ba 100644 --- a/src/__tests__/client.ts +++ b/src/__tests__/client.ts @@ -25,7 +25,7 @@ function withClient(f: (c: hrana.Client) => Promise): () => Promise if (isWs) { client = hrana.openWs(url, jwt, 3); } else if (isHttp) { - client = hrana.openHttp(url, jwt, undefined, 3); + client = hrana.openHttp(url, jwt, undefined, undefined, 3); } else { throw new Error("expected either ws or http URL"); } @@ -50,7 +50,7 @@ function withWsClient(f: (c: hrana.WsClient) => Promise): () => Promise Promise): () => Promise { return async () => { - const client = hrana.openHttp(url, jwt, undefined, 3); + const client = hrana.openHttp(url, jwt, undefined, undefined, 3); try { await f(client); } finally { @@ -878,7 +878,7 @@ for (const useCursor of [false, true]) { client.close(); } } else if (isHttp) { - const client = hrana.openHttp(url, jwt, undefined, 3); + const client = hrana.openHttp(url, jwt, undefined, undefined, 3); try { const stream = client.openStream(); await f(stream, stream); @@ -981,7 +981,7 @@ test("getVersion()", withClient(async (c) => { return fetch(request); } - const c = hrana.openHttp(url, jwt, customFetch, 3); + const c = hrana.openHttp(url, jwt, customFetch, undefined, 3); try { const s = c.openStream(); const res = await s.queryValue("SELECT 1"); @@ -998,7 +998,7 @@ test("getVersion()", withClient(async (c) => { throw new Error("testing exception thrown from customFetch()"); } - const c = hrana.openHttp(url, jwt, customFetch, 3); + const c = hrana.openHttp(url, jwt, customFetch, undefined, 3); try { const s = c.openStream(); await expect(s.queryValue("SELECT 1")).rejects @@ -1013,7 +1013,7 @@ test("getVersion()", withClient(async (c) => { return Promise.reject(new Error("testing rejection returned from customFetch()")); } - const c = hrana.openHttp(url, jwt, customFetch, 3); + const c = hrana.openHttp(url, jwt, customFetch, undefined, 3); try { const s = c.openStream(); await expect(s.queryValue("SELECT 1")).rejects