diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 68c3cf2..c26566e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -39,7 +39,7 @@ jobs:
- name: Upload Code Coverage
uses: codecov/codecov-action@v4
env:
- CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
examples-test:
needs: code-test
@@ -123,21 +123,13 @@ jobs:
exit 1
- name: remove with-javascript-express
- run: rm -rf examples/with-javascript-express
+ run: rm -rf examples/with-javascript-express/*
- name: node with-plain-javascript
working-directory: examples/with-plain-javascript
run: |
npm i
- app_log=$(mktemp)
- npm start > "$app_log" 2>&1 &
-
- app_pid=$!
- sleep 3
- kill "$app_pid" 2>/dev/null
-
- node_test=$(<"$app_log")
- rm "$app_log"
+ node_test=$(npm start)
if [[ "$node_test" == *"Composer: 'Angus Young, Malcolm Young, Brian Johnson',"* ]]; then
echo "✅ node with-plain-javascript test passed"
@@ -152,15 +144,8 @@ jobs:
if [ "$RUNNER_OS" != "Windows" ]; then
bun i #re-installing dependencies in windows with bash causes a panic
fi
- app_log=$(mktemp)
- bun start > "$app_log" 2>&1 &
-
- app_pid=$!
- sleep 3
- kill "$app_pid" 2>/dev/null
-
- bun_test=$(<"$app_log")
- rm "$app_log"
+
+ bun_test=$(bun start)
if [[ "$bun_test" == *"Composer: 'Angus Young, Malcolm Young, Brian Johnson',"* ]]; then
echo "✅ bun with-plain-javascript test passed"
@@ -172,15 +157,7 @@ jobs:
- name: deno with-plain-javascript
working-directory: examples/with-plain-javascript
run: |
- app_log=$(mktemp)
- deno run start > "$app_log" 2>&1 &
-
- app_pid=$!
- sleep 3
- kill "$app_pid" 2>/dev/null
-
- deno_test=$(<"$app_log")
- rm "$app_log"
+ deno_test=$(deno run start)
if [[ "$deno_test" == *"Composer: 'Angus Young, Malcolm Young, Brian Johnson',"* ]]; then
echo "✅ deno with-plain-javascript test passed"
@@ -191,7 +168,7 @@ jobs:
- name: remove with-plain-javascript
if: matrix.os != 'windows-latest' #kill command doesn not work on windows with bash, we can skip this step
- run: rm -rf examples/with-plain-javascript
+ run: rm -rf examples/with-plain-javascript/*
- name: node with-typescript-knex
working-directory: examples/with-typescript-knex
@@ -222,7 +199,7 @@ jobs:
exit 1
- name: remove with-typescript-knex
- run: rm -rf examples/with-typescript-knex
+ run: rm -rf examples/with-typescript-knex/*
- name: node with-typescript-nextjs
working-directory: examples/with-typescript-nextjs
@@ -275,8 +252,7 @@ jobs:
exit 1
- name: remove with-typescript-nextjs
- if: matrix.os != 'ubuntu-latest' #rm: cannot remove examples/with-typescript-nextjs: Directory not empty
- run: rm -rf examples/with-typescript-nextjs
+ run: rm -rf examples/with-typescript-nextjs/*
- name: node with-javascript-vite
if: matrix.os != 'LinuxARM64'
@@ -305,7 +281,7 @@ jobs:
PW_DISABLE_TS_ESM: true
- name: remove with-javascript-vite
- run: rm -rf examples/with-javascript-vite
+ run: rm -rf examples/with-javascript-vite/*
- name: node with-javascript-browser
if: matrix.os != 'LinuxARM64'
@@ -326,7 +302,7 @@ jobs:
command: cd examples/with-javascript-browser && deno --allow-all test.cjs
- name: remove with-javascript-browser
- run: rm -rf examples/with-javascript-browser
+ run: rm -rf examples/with-javascript-browser/*
rn-ios-test:
needs: code-test
@@ -362,7 +338,8 @@ jobs:
xcode-version: 14.3
- name: build driver
- run: npm i && npm run build && echo "DRIVER=$(npm pack --json | jq '.[0].filename')" >> $GITHUB_ENV
+ # distutils is required for the driver build (dep. `node-gyp`) and it was removed since python 3.13
+ run: brew install python-setuptools && npm i && npm run build && echo "DRIVER=$(npm pack --json | jq '.[0].filename')" >> $GITHUB_ENV
- name: install driver
working-directory: examples/with-typescript-react-native
@@ -489,7 +466,8 @@ jobs:
xcode-version: 14.3
- name: build driver
- run: npm i && npm run build && echo "DRIVER=$(npm pack --json | jq '.[0].filename')" >> $GITHUB_ENV
+ # distutils is required for the driver build (dep. `node-gyp`) and it was removed since python 3.13
+ run: brew install python-setuptools && npm i && npm run build && echo "DRIVER=$(npm pack --json | jq '.[0].filename')" >> $GITHUB_ENV
- name: install driver
working-directory: examples/with-javascript-expo
diff --git a/examples/with-javascript-browser/index.html b/examples/with-javascript-browser/index.html
index e5b9656..e4d1bc4 100644
--- a/examples/with-javascript-browser/index.html
+++ b/examples/with-javascript-browser/index.html
@@ -44,33 +44,28 @@
Results:
messages.prepend(item);
};
- // socket is connected the first time the sql button is clicked and stays connected until the page is refreshed
- var database = null;
-
sendButton.addEventListener('click', () => {
- if (!database || !database.isConnected()) {
- // Get the input element by ID
- var connectionStringinputElement = document.getElementById('connectionStringInput');
- var connectionstring = connectionStringinputElement.value;
- // connect via websocket to the gateway on the same server
- const connectionConfig = {
- gatewayUrl: `${window.location.protocol === 'https:' ? 'wss' : 'ws'
- }://${window.location.hostname}:4000`,
- connectionstring: connectionstring,
- };
- database = new window.sqlitecloud.Database(
- connectionConfig,
- (error) => {
- if (error) {
- database = null;
- appendMessage(`connection error: ${error}`);
- } else {
- console.log('connected');
- appendMessage(`connected`);
- }
+ // Get the input element by ID
+ var connectionStringinputElement = document.getElementById('connectionStringInput');
+ var connectionstring = connectionStringinputElement.value;
+ // connect via websocket to the gateway on the same server
+ const connectionConfig = {
+ gatewayUrl: `${window.location.protocol === 'https:' ? 'wss' : 'ws'
+ }://${window.location.hostname}:4000`,
+ connectionstring: connectionstring,
+ };
+ var database = new window.sqlitecloud.Database(
+ connectionConfig,
+ (error) => {
+ if (error) {
+ database = null;
+ appendMessage(`connection error: ${error}`);
+ } else {
+ console.log('connected');
+ appendMessage(`connected`);
}
- );
- }
+ }
+ );
var messageInputElement = document.getElementById('messageInput');
const sql = messageInputElement.value;
diff --git a/examples/with-javascript-expo/components/AddTaskModal.js b/examples/with-javascript-expo/components/AddTaskModal.js
index 57b6e47..757ccf1 100644
--- a/examples/with-javascript-expo/components/AddTaskModal.js
+++ b/examples/with-javascript-expo/components/AddTaskModal.js
@@ -29,11 +29,15 @@ export default AddTaskModal = ({
};
const getTags = async () => {
+ let db = null;
try {
- const tags = await getDbConnection().sql("SELECT * FROM tags");
+ db = getDbConnection();
+ const tags = await db.sql("SELECT * FROM tags");
setTagsList(tags);
} catch (error) {
console.error("Error getting tags", error);
+ } finally {
+ db?.close();
}
};
diff --git a/examples/with-javascript-expo/db/dbConnection.js b/examples/with-javascript-expo/db/dbConnection.js
index 26958e4..4d0b8dd 100644
--- a/examples/with-javascript-expo/db/dbConnection.js
+++ b/examples/with-javascript-expo/db/dbConnection.js
@@ -1,14 +1,7 @@
import { DATABASE_URL } from "@env";
import { Database } from "@sqlitecloud/drivers";
-/**
- * @type {Database}
- */
-let database = null;
export default function getDbConnection() {
- if (!database || !database.isConnected()) {
- database = new Database(DATABASE_URL);
- }
- return database;
+ return new Database(DATABASE_URL);
}
\ No newline at end of file
diff --git a/examples/with-javascript-expo/hooks/useCategories.js b/examples/with-javascript-expo/hooks/useCategories.js
index db425b0..fe0502d 100644
--- a/examples/with-javascript-expo/hooks/useCategories.js
+++ b/examples/with-javascript-expo/hooks/useCategories.js
@@ -1,78 +1,73 @@
-import { useState, useEffect } from "react";
-import getDbConnection from "../db/dbConnection";
+import { useState, useEffect } from 'react'
+import getDbConnection from '../db/dbConnection'
const useCategories = () => {
- const [moreCategories, setMoreCategories] = useState(["Work", "Personal"]);
+ const [moreCategories, setMoreCategories] = useState(['Work', 'Personal'])
const getCategories = async () => {
+ let db = null;
try {
- const tags = await getDbConnection().sql("SELECT * FROM tags");
- const filteredTags = tags.filter((tag) => {
- return tag["name"] !== "Work" && tag["name"] !== "Personal";
- });
- setMoreCategories((prevCategories) => [
- ...prevCategories,
- ...filteredTags.map((tag) => tag.name),
- ]);
+ db = getDbConnection();
+ const tags = await db.sql('SELECT * FROM tags')
+ const filteredTags = tags.filter(tag => {
+ return tag['name'] !== 'Work' && tag['name'] !== 'Personal'
+ })
+ setMoreCategories(prevCategories => [...prevCategories, ...filteredTags.map(tag => tag.name)])
} catch (error) {
- console.error("Error getting tags/categories", error);
+ console.error('Error getting tags/categories', error)
}
- };
+ }
- const addCategory = async (newCategory) => {
+ const addCategory = async newCategory => {
+ let db = null;
try {
- await getDbConnection().sql(
- "INSERT INTO tags (name) VALUES (?) RETURNING *",
- newCategory
- );
- setMoreCategories((prevCategories) => [...prevCategories, newCategory]);
+ db = getDbConnection();
+ await db.sql('INSERT INTO tags (name) VALUES (?) RETURNING *', newCategory)
+ setMoreCategories(prevCategories => [...prevCategories, newCategory])
} catch (error) {
- console.error("Error adding category", error);
+ console.error('Error adding category', error)
+ } finally {
+ db?.close();
}
- };
+ }
const initializeTables = async () => {
+ let db = null;
try {
- const createTasksTable = await getDbConnection().sql(
- "CREATE TABLE IF NOT EXISTS tasks (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, isCompleted INT NOT NULL);"
- );
+ db = getDbConnection();
+ const createTasksTable = await db.sql(
+ 'CREATE TABLE IF NOT EXISTS tasks (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, isCompleted INT NOT NULL);'
+ )
- const createTagsTable = await getDbConnection().sql(
- "CREATE TABLE IF NOT EXISTS tags (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, UNIQUE(name));"
- );
+ const createTagsTable = await db.sql('CREATE TABLE IF NOT EXISTS tags (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, UNIQUE(name));')
- const createTagsTasksTable = await getDbConnection().sql(
- "CREATE TABLE IF NOT EXISTS tasks_tags (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, task_id INTEGER NOT NULL, tag_id INTEGER NOT NULL, FOREIGN KEY (task_id) REFERENCES tasks(id), FOREIGN KEY (tag_id) REFERENCES tags(id));"
- );
+ const createTagsTasksTable = await db.sql(
+ 'CREATE TABLE IF NOT EXISTS tasks_tags (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, task_id INTEGER NOT NULL, tag_id INTEGER NOT NULL, FOREIGN KEY (task_id) REFERENCES tasks(id), FOREIGN KEY (tag_id) REFERENCES tags(id));'
+ )
- if (
- createTasksTable === "OK" &&
- createTagsTable === "OK" &&
- createTagsTasksTable === "OK"
- ) {
- console.log("Successfully created tables");
+ if (createTasksTable === 'OK' && createTagsTable === 'OK' && createTagsTasksTable === 'OK') {
+ console.log('Successfully created tables')
- await getDbConnection().sql("INSERT OR IGNORE INTO tags (name) VALUES (?)", "Work");
- await getDbConnection().sql(
- "INSERT OR IGNORE INTO tags (name) VALUES (?)",
- "Personal"
- );
- getCategories();
+ await db.sql('INSERT OR IGNORE INTO tags (name) VALUES (?)', 'Work')
+ await db.sql('INSERT OR IGNORE INTO tags (name) VALUES (?)', 'Personal')
+ getCategories()
}
} catch (error) {
- console.error("Error creating tables", error);
+ console.error('Error creating tables', error)
+ } finally {
+ db?.close();
}
- };
+ }
useEffect(() => {
- initializeTables();
- }, []);
+ initializeTables()
+ }, [])
return {
moreCategories,
addCategory,
- getCategories,
- };
-};
+ getCategories
+ }
+}
-export default useCategories;
+export default useCategories
diff --git a/examples/with-javascript-expo/hooks/useTasks.js b/examples/with-javascript-expo/hooks/useTasks.js
index 0a7ccad..6e7906e 100644
--- a/examples/with-javascript-expo/hooks/useTasks.js
+++ b/examples/with-javascript-expo/hooks/useTasks.js
@@ -5,10 +5,12 @@ const useTasks = (tag = null) => {
const [taskList, setTaskList] = useState([]);
const getTasks = useCallback(async () => {
+ let db = null;
try {
let result;
+ db = getDbConnection();
if (tag) {
- result = await getDbConnection().sql(
+ result = await db.sql(
`
SELECT tasks.*, tags.id AS tag_id, tags.name AS tag_name
FROM tasks
@@ -19,7 +21,7 @@ const useTasks = (tag = null) => {
);
setTaskList(result);
} else {
- result = await getDbConnection().sql(`
+ result = await db.sql(`
SELECT tasks.*, tags.id AS tag_id, tags.name AS tag_name
FROM tasks
JOIN tasks_tags ON tasks.id = tasks_tags.task_id
@@ -28,12 +30,16 @@ const useTasks = (tag = null) => {
}
} catch (error) {
console.error("Error getting tasks", error);
+ } finally {
+ db?.close();
}
}, [tag]);
const updateTask = async (completedStatus, taskId) => {
+ let db = null;
try {
- await getDbConnection().sql(
+ db = getDbConnection();
+ await db.sql(
"UPDATE tasks SET isCompleted=? WHERE id=? RETURNING *",
completedStatus,
taskId
@@ -41,13 +47,17 @@ const useTasks = (tag = null) => {
getTasks();
} catch (error) {
console.error("Error updating tasks", error);
+ } finally {
+ db?.close();
}
};
const addTaskTag = async (newTask, tag) => {
+ let db = null;
try {
+ db = getDbConnection();
if (tag.id) {
- const addNewTask = await getDbConnection().sql(
+ const addNewTask = await db.sql(
"INSERT INTO tasks (title, isCompleted) VALUES (?, ?) RETURNING *",
newTask.title,
newTask.isCompleted
@@ -55,13 +65,13 @@ const useTasks = (tag = null) => {
addNewTask[0].tag_id = tag.id;
addNewTask[0].tag_name = tag.name;
setTaskList([...taskList, addNewTask[0]]);
- await getDbConnection().sql(
+ await db.sql(
"INSERT INTO tasks_tags (task_id, tag_id) VALUES (?, ?)",
addNewTask[0].id,
tag.id
);
} else {
- const addNewTaskNoTag = await getDbConnection().sql(
+ const addNewTaskNoTag = await db.sql(
"INSERT INTO tasks (title, isCompleted) VALUES (?, ?) RETURNING *",
newTask.title,
newTask.isCompleted
@@ -70,17 +80,23 @@ const useTasks = (tag = null) => {
}
} catch (error) {
console.error("Error adding task to database", error);
+ } finally {
+ db?.close();
}
};
const deleteTask = async (taskId) => {
+ let db = null;
try {
- await getDbConnection().sql("DELETE FROM tasks_tags WHERE task_id=?", taskId);
- const result = await getDbConnection().sql("DELETE FROM tasks WHERE id=?", taskId);
+ db = getDbConnection();
+ await db.sql("DELETE FROM tasks_tags WHERE task_id=?", taskId);
+ const result = await db.sql("DELETE FROM tasks WHERE id=?", taskId);
console.log(`Deleted ${result.totalChanges} task`);
getTasks();
} catch (error) {
console.error("Error deleting task", error);
+ } finally {
+ db?.close();
}
};
diff --git a/examples/with-javascript-express/app.js b/examples/with-javascript-express/app.js
index ead1269..80af967 100644
--- a/examples/with-javascript-express/app.js
+++ b/examples/with-javascript-express/app.js
@@ -14,9 +14,18 @@ app.use(express.json())
/* http://localhost:3001/ returns chinook tracks as json */
app.get('/', async function (req, res, next) {
- var database = new sqlitecloud.Database(DATABASE_URL)
- var tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
- res.send({ tracks })
+ var database = null
+ try {
+ database = new sqlitecloud.Database(DATABASE_URL)
+ var tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
+ res.send({ tracks })
+ } catch (error) {
+ res.send({ error: error.message })
+ } finally {
+ if (database) {
+ database.close()
+ }
+ }
})
const port = process.env.PORT || 3000
diff --git a/examples/with-javascript-vite/src/App.jsx b/examples/with-javascript-vite/src/App.jsx
index cb79747..2d05d69 100644
--- a/examples/with-javascript-vite/src/App.jsx
+++ b/examples/with-javascript-vite/src/App.jsx
@@ -1,30 +1,28 @@
import { useEffect, useState } from "react";
import { Database } from "@sqlitecloud/drivers";
-let db = null
-
-function getDatabase() {
- if (!db || !db.isConnected()) {
- db = new Database(import.meta.env.VITE_DATABASE_URL);
- }
-
- return db;
-}
-
function App() {
const [data, setData] = useState([]);
const getAlbums = async () => {
- const result = await getDatabase().sql(`
- USE DATABASE chinook.sqlite;
- SELECT albums.AlbumId as id, albums.Title as title, artists.name as artist
- FROM albums
- INNER JOIN artists
- WHERE artists.ArtistId = albums.ArtistId
- LIMIT 20;
- `);
- setData(result);
+ let database = null;
+ try {
+ database = new Database(import.meta.env.VITE_DATABASE_URL)
+ const result = await database.sql(`
+ USE DATABASE chinook.sqlite;
+ SELECT albums.AlbumId as id, albums.Title as title, artists.name as artist
+ FROM albums
+ INNER JOIN artists
+ WHERE artists.ArtistId = albums.ArtistId
+ LIMIT 20;
+ `);
+ setData(result);
+ } catch (error) {
+ console.error("Error getting albums", error);
+ } finally {
+ database.close();
+ }
};
useEffect(() => {
diff --git a/examples/with-plain-javascript/app.js b/examples/with-plain-javascript/app.js
index f0e44c4..14040e5 100644
--- a/examples/with-plain-javascript/app.js
+++ b/examples/with-plain-javascript/app.js
@@ -9,12 +9,19 @@ var DATABASE_URL = process.env.DATABASE_URL
console.assert(DATABASE_URL, 'DATABASE_URL environment variable not set in .env')
async function selectTracks() {
- // create a connection with sqlitecloud
- var database = new sqlitecloud.Database(DATABASE_URL)
+ var database = null
+ try {
+ // create a connection with sqlitecloud
+ database = new sqlitecloud.Database(DATABASE_URL)
- // run async query
- var tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
- console.log(`selectTracks returned:`, tracks)
+ // run async query
+ var tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
+ console.log(`selectTracks returned:`, tracks)
+ } catch (error) {
+ console.error(`selectTracks error:`, error)
+ } finally {
+ database.close()
+ }
// You can also use all the regular sqlite3 api with callbacks, see:
// https://docs.sqlitecloud.io/docs/sdk/js/intro
diff --git a/examples/with-typescript-nextjs/app/api/hello/route.ts b/examples/with-typescript-nextjs/app/api/hello/route.ts
index a191eff..983841f 100644
--- a/examples/with-typescript-nextjs/app/api/hello/route.ts
+++ b/examples/with-typescript-nextjs/app/api/hello/route.ts
@@ -12,11 +12,18 @@ console.assert(DATABASE_URL, 'Please configure a .env file with DATABASE_URL poi
// route for /api/hello
export async function GET(request: NextRequest) {
// connect to database using connection string provided in https://dashboard.sqlitecloud.io/
- const database = new Database(DATABASE_URL)
+ let database
+ try {
+ database = new Database(DATABASE_URL)
- // retrieve rows from chinook database using a plain SQL query
- const tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
+ // retrieve rows from chinook database using a plain SQL query
+ const tracks = await database.sql('USE DATABASE chinook.sqlite; SELECT * FROM tracks LIMIT 20;')
- // return as json response
- return NextResponse.json<{ data: any }>({ data: tracks })
+ // return as json response
+ return NextResponse.json<{ data: any }>({ data: tracks })
+ } catch (error) {
+ return NextResponse.json({ error }, { status: 500 })
+ } finally {
+ database?.close()
+ }
}
diff --git a/examples/with-typescript-react-native/App.tsx b/examples/with-typescript-react-native/App.tsx
index b71df81..454e2dd 100644
--- a/examples/with-typescript-react-native/App.tsx
+++ b/examples/with-typescript-react-native/App.tsx
@@ -8,12 +8,19 @@ export default function App() {
useEffect(() => {
async function getAlbums() {
- const db = new Database(`${DATABASE_URL}`);
+ let db = null;
+ try {
+ db = new Database(`${DATABASE_URL}`);
- const result =
- await db.sql('USE DATABASE chinook.sqlite; SELECT albums.AlbumId as id, albums.Title as title, artists.name as artist FROM albums INNER JOIN artists WHERE artists.ArtistId = albums.ArtistId LIMIT 20;');
+ const result =
+ await db.sql('USE DATABASE chinook.sqlite; SELECT albums.AlbumId as id, albums.Title as title, artists.name as artist FROM albums INNER JOIN artists WHERE artists.ArtistId = albums.ArtistId LIMIT 20;');
- setAlbums(result);
+ setAlbums(result);
+ } catch (error) {
+ console.error(error);
+ } finally {
+ db?.close();
+ }
}
getAlbums();
diff --git a/package-lock.json b/package-lock.json
index 4ed0005..b1e7e3d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@sqlitecloud/drivers",
- "version": "1.0.422",
+ "version": "1.0.438",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@sqlitecloud/drivers",
- "version": "1.0.422",
+ "version": "1.0.438",
"license": "MIT",
"dependencies": {
"buffer": "^6.0.3",
@@ -18,20 +18,20 @@
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/lz4": "^0.6.4",
- "@types/node": "^22.13.4",
+ "@types/node": "^22.13.8",
"@types/whatwg-url": "^13.0.0",
"dotenv": "^16.4.7",
"dotenv-cli": "^8.0.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-html-reporter": "^4.0.1",
- "prettier": "^3.5.2",
+ "prettier": "^3.5.3",
"sqlite3": "^5.1.7",
- "ts-jest": "^29.2.5",
+ "ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
- "typedoc": "^0.27.7",
+ "typedoc": "^0.27.9",
"typedoc-plugin-markdown": "^4.4.2",
- "typescript": "^5.7.3",
+ "typescript": "^5.8.2",
"webpack": "^5.98.0",
"webpack-cli": "^6.0.1"
},
@@ -8345,9 +8345,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.13.4",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
- "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==",
+ "version": "22.13.8",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz",
+ "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
@@ -13858,9 +13858,9 @@
}
},
"node_modules/prettier": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
- "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -14589,9 +14589,9 @@
}
},
"node_modules/semver": {
- "version": "7.6.3",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
- "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -15384,9 +15384,9 @@
}
},
"node_modules/ts-jest": {
- "version": "29.2.5",
- "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
- "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==",
+ "version": "29.2.6",
+ "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz",
+ "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -15397,7 +15397,7 @@
"json5": "^2.2.3",
"lodash.memoize": "^4.1.2",
"make-error": "^1.3.6",
- "semver": "^7.6.3",
+ "semver": "^7.7.1",
"yargs-parser": "^21.1.1"
},
"bin": {
@@ -15498,9 +15498,9 @@
}
},
"node_modules/typedoc": {
- "version": "0.27.7",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.7.tgz",
- "integrity": "sha512-K/JaUPX18+61W3VXek1cWC5gwmuLvYTOXJzBvD9W7jFvbPnefRnCHQCEPw7MSNrP/Hj7JJrhZtDDLKdcYm6ucg==",
+ "version": "0.27.9",
+ "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.27.9.tgz",
+ "integrity": "sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
@@ -15517,7 +15517,7 @@
"node": ">= 18"
},
"peerDependencies": {
- "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x"
+ "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x"
}
},
"node_modules/typedoc-plugin-markdown": {
@@ -15556,9 +15556,9 @@
}
},
"node_modules/typescript": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
- "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "version": "5.8.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"devOptional": true,
"license": "Apache-2.0",
"bin": {
diff --git a/package.json b/package.json
index 0b2ff65..df49d59 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@sqlitecloud/drivers",
- "version": "1.0.422",
+ "version": "1.0.438",
"description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@@ -57,20 +57,20 @@
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/lz4": "^0.6.4",
- "@types/node": "^22.13.4",
+ "@types/node": "^22.13.8",
"@types/whatwg-url": "^13.0.0",
"dotenv": "^16.4.7",
"dotenv-cli": "^8.0.0",
"husky": "^9.1.7",
"jest": "^29.7.0",
"jest-html-reporter": "^4.0.1",
- "prettier": "^3.5.2",
+ "prettier": "^3.5.3",
"sqlite3": "^5.1.7",
- "ts-jest": "^29.2.5",
+ "ts-jest": "^29.2.6",
"ts-node": "^10.9.2",
- "typedoc": "^0.27.7",
+ "typedoc": "^0.27.9",
"typedoc-plugin-markdown": "^4.4.2",
- "typescript": "^5.7.3",
+ "typescript": "^5.8.2",
"webpack": "^5.98.0",
"webpack-cli": "^6.0.1"
},
diff --git a/src/drivers/database.ts b/src/drivers/database.ts
index 797a221..80aa1f5 100644
--- a/src/drivers/database.ts
+++ b/src/drivers/database.ts
@@ -96,6 +96,7 @@ export class Database extends EventEmitter {
})
.catch(error => {
this.handleError(error, callback)
+ this.close()
done(error)
})
})
@@ -116,6 +117,7 @@ export class Database extends EventEmitter {
})
.catch(error => {
this.handleError(error, callback)
+ this.close()
done(error)
})
})
@@ -128,21 +130,20 @@ export class Database extends EventEmitter {
// we don't wont to silently open a new connection after a disconnession
if (this.connection && this.connection.connected) {
- this.connection.sendCommands(command, callback)
+ this.connection.sendCommands(command, (error, results) => {
+ callback?.call(this, error, results)
+ done(error)
+ })
} else {
error = new SQLiteCloudError('Connection unavailable. Maybe it got disconnected?', { errorCode: 'ERR_CONNECTION_NOT_ESTABLISHED' })
- this.handleError(error, callback)
+ callback?.call(this, error, null)
+ done(error)
}
-
- done(error)
})
}
/** Handles an error by closing the connection, calling the callback and/or emitting an error event */
private handleError(error: Error, callback?: ConnectionCallback): void {
- // an errored connection is thrown out
- this.connection?.close()
-
if (callback) {
callback.call(this, error)
} else {
@@ -382,11 +383,15 @@ export class Database extends EventEmitter {
* parameters is emitted, regardless of whether a callback was provided or not.
*/
public close(callback?: ConnectionCallback): void {
- this.operations.clear()
- this.connection?.close()
+ this.operations.enqueue(done => {
+ this.connection?.close()
+
+ callback?.call(this, null)
+ this.emitEvent('close')
- callback?.call(this, null)
- this.emitEvent('close')
+ this.operations.clear()
+ done(null)
+ })
}
/**
diff --git a/src/drivers/utilities.ts b/src/drivers/utilities.ts
index d0fe859..b7075ab 100644
--- a/src/drivers/utilities.ts
+++ b/src/drivers/utilities.ts
@@ -219,11 +219,26 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf
})
const config: SQLiteCloudConfig = {
+ ...options,
username: decodeURIComponent(url.username),
password: decodeURIComponent(url.password),
+ password_hashed: options.password_hashed ? parseBoolean(options.password_hashed) : undefined,
host: url.hostname,
+ // type cast values
port: url.port ? parseInt(url.port) : undefined,
- ...options
+ insecure: options.insecure ? parseBoolean(options.insecure) : undefined,
+ timeout: options.timeout ? parseInt(options.timeout) : undefined,
+ zerotext: options.zerotext ? parseBoolean(options.zerotext) : undefined,
+ create: options.create ? parseBoolean(options.create) : undefined,
+ memory: options.memory ? parseBoolean(options.memory) : undefined,
+ compression: options.compression ? parseBoolean(options.compression) : undefined,
+ non_linearizable: options.non_linearizable ? parseBoolean(options.non_linearizable) : undefined,
+ noblob: options.noblob ? parseBoolean(options.noblob) : undefined,
+ maxdata: options.maxdata ? parseInt(options.maxdata) : undefined,
+ maxrows: options.maxrows ? parseInt(options.maxrows) : undefined,
+ maxrowset: options.maxrowset ? parseInt(options.maxrowset) : undefined,
+ usewebsocket: options.usewebsocket ? parseBoolean(options.usewebsocket) : undefined,
+ verbose: options.verbose ? parseBoolean(options.verbose) : undefined
}
// either you use an apikey or username and password
diff --git a/test/connection-ws.test.ts b/test/connection-ws.test.ts
index 2772fd3..3cf2fd5 100644
--- a/test/connection-ws.test.ts
+++ b/test/connection-ws.test.ts
@@ -2,19 +2,17 @@
* connection-ws.test.ts - test connection via socket.io based gateway
*/
-import { SQLiteCloudError } from '../src/index'
import { SQLiteCloudConnection } from '../src/drivers/connection'
import { SQLiteCloudWebsocketConnection } from '../src/drivers/connection-ws'
+import { SQLiteCloudCommand } from '../src/drivers/types'
+import { SQLiteCloudError } from '../src/index'
import {
- //
- CHINOOK_DATABASE_URL,
- LONG_TIMEOUT,
+ EXPECT_SPEED_MS,
getChinookConfig,
getChinookWebsocketConnection,
- WARN_SPEED_MS,
- EXPECT_SPEED_MS
+ LONG_TIMEOUT,
+ WARN_SPEED_MS
} from './shared'
-import { SQLiteCloudCommand } from '../src/drivers/types'
describe('connection-ws', () => {
let chinook: SQLiteCloudConnection
diff --git a/test/database.test.ts b/test/database.test.ts
index ef46f81..ace2400 100644
--- a/test/database.test.ts
+++ b/test/database.test.ts
@@ -246,6 +246,33 @@ describe('Database.get', () => {
})
})
})
+
+ it('close() is executed after the previous commands', done => {
+ // the database enqueue the close command
+ const chinook = getChinookDatabase()
+ chinook.get('SELECT * FROM tracks', (err: Error, row?: SQLiteCloudRow) => {
+ expect(err).toBeNull()
+ expect(row).toBeDefined()
+ expect(row).toMatchObject({
+ AlbumId: 1,
+ Bytes: 11170334,
+ Composer: 'Angus Young, Malcolm Young, Brian Johnson',
+ GenreId: 1,
+ MediaTypeId: 1,
+ Milliseconds: 343719,
+ Name: 'For Those About To Rock (We Salute You)',
+ TrackId: 1,
+ UnitPrice: 0.99
+ })
+ })
+
+ // call close() right after the execution
+ // of the query not in its callback
+ chinook.close(error => {
+ expect(error).toBeNull()
+ done()
+ })
+ })
})
describe('Database.each', () => {
diff --git a/test/utilities.test.ts b/test/utilities.test.ts
index c524db7..423b3ca 100644
--- a/test/utilities.test.ts
+++ b/test/utilities.test.ts
@@ -57,7 +57,7 @@ describe('parseconnectionstring', () => {
expect(config4).toEqual({
host: 'host',
apikey: 'yyy',
- maxrows: '42' // only parsing here, validation is later in validateConfiguration
+ maxrows: 42 // only parsing here, validation is later in validateConfiguration
})
})
@@ -138,6 +138,30 @@ describe('parseconnectionstring', () => {
database: 'database'
})
})
+
+ it('should parse connection with insecure as bool or number', () => {
+ let connectionstring = `sqlitecloud://host:1234/database?insecure=true`
+ let config = parseconnectionstring(connectionstring)
+
+ expect(config.insecure).toBe(true)
+
+ connectionstring = `sqlitecloud://host:1234/database?insecure=1`
+ config = parseconnectionstring(connectionstring)
+
+ expect(config.insecure).toBe(true)
+
+ connectionstring = `sqlitecloud://host:1234/database?insecure=0`
+ config = parseconnectionstring(connectionstring)
+
+ expect(config.insecure).toBe(false)
+ })
+
+ it('should parse connection with timeout as number', () => {
+ let connectionstring = `sqlitecloud://host:1234/database?timeout=123`
+ let config = parseconnectionstring(connectionstring)
+
+ expect(config.timeout).toBe(123)
+ })
})
describe('getTestingDatabaseName', () => {