Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sqlite.org/sqlite-wasm",
"version": "3.51.2-build2",
"version": "3.51.2-build3",
"description": "SQLite Wasm conveniently wrapped as an ES Module.",
"type": "module",
"repository": {
Expand Down
53 changes: 53 additions & 0 deletions src/__tests__/bundler-compatibility.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, test, expect } from 'vitest';
import { execSync } from 'node:child_process';
import path from 'node:path';
import fs from 'node:fs';

describe('Vite bundler compatibility', () => {
test('should rename sqlite3.wasm with a hash and update the import URL', () => {
const testDir = path.resolve(__dirname, 'vite-repro');
const distDir = path.resolve(testDir, 'dist');

// Clean up previous build
if (fs.existsSync(distDir)) {
fs.rmSync(distDir, { recursive: true, force: true });
}

// Run vite build
execSync('npx vite build --logLevel error', {
cwd: testDir,
stdio: 'inherit',
});

// 1. Check if hashed WASM file exists in dist/assets
const assetsDir = path.resolve(distDir, 'assets');
const files = fs.readdirSync(assetsDir);
const wasmFile = files.find(
(f) => f.startsWith('sqlite3-') && f.endsWith('.wasm'),
);

expect(wasmFile).toBeDefined();

// 2. Check if the JS bundle contains the hashed WASM filename
const assetsDirJs = path.resolve(distDir, 'assets');
const jsFiles = fs
.readdirSync(assetsDirJs)
.filter((f) => f.endsWith('.js'));
const mainBundle = jsFiles.find((f) => f.startsWith('index-'));
expect(mainBundle).toBeDefined();

const bundleContent = fs.readFileSync(
path.resolve(assetsDirJs, mainBundle),
'utf8',
);

// It should contain something like: new URL("/assets/sqlite3-hash.wasm", import.meta.url)
expect(bundleContent).toContain(wasmFile);

// Specifically check that it's part of a new URL call or at least correctly referenced
const urlPattern = new RegExp(
`new URL\\(".*${wasmFile}",\\s*import\\.meta\\.url\\)`,
);
expect(bundleContent).toMatch(urlPattern);
});
});
24 changes: 24 additions & 0 deletions src/__tests__/sqlite3-node.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,30 @@ test('Node.js build sanity check', async () => {
expect(
typeof db.selectValue('SELECT sqlite_offset(id) FROM off_test'),
).toBe('number');

// 13. Blobs
const blobData = new Uint8Array([0x00, 0xff, 0xaa, 0x55]);
db.exec({
sql: 'CREATE TABLE blobs (data BLOB)',
});
db.exec({
sql: 'INSERT INTO blobs (data) VALUES (?)',
bind: [blobData],
});
const retrievedBlob = db.selectValue('SELECT data FROM blobs');
expect(retrievedBlob).toBeInstanceOf(Uint8Array);
expect(retrievedBlob).toEqual(blobData);

// 14. Error handling
expect(() => {
db.exec('INVALID SQL');
}).toThrow();

db.exec('CREATE TABLE unique_test (id INTEGER PRIMARY KEY)');
db.exec('INSERT INTO unique_test VALUES (1)');
expect(() => {
db.exec('INSERT INTO unique_test VALUES (1)');
}).toThrow(/UNIQUE constraint failed/);
} finally {
// 11. Close the database
db.close();
Expand Down
24 changes: 24 additions & 0 deletions src/__tests__/sqlite3-oo1.browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,30 @@ test('Bundler-friendly OO1 API sanity check (browser)', async () => {
expect(
typeof db.selectValue('SELECT sqlite_offset(id) FROM off_test'),
).toBe('number');

// 13. Blobs
const blobData = new Uint8Array([0x00, 0xff, 0xaa, 0x55]);
db.exec({
sql: 'CREATE TABLE blobs (data BLOB)',
});
db.exec({
sql: 'INSERT INTO blobs (data) VALUES (?)',
bind: [blobData],
});
const retrievedBlob = db.selectValue('SELECT data FROM blobs');
expect(retrievedBlob).toBeInstanceOf(Uint8Array);
expect(retrievedBlob).toEqual(blobData);

// 14. Error handling
expect(() => {
db.exec('INVALID SQL');
}).toThrow();

db.exec('CREATE TABLE unique_test (id INTEGER PRIMARY KEY)');
db.exec('INSERT INTO unique_test VALUES (1)');
expect(() => {
db.exec('INSERT INTO unique_test VALUES (1)');
}).toThrow(/UNIQUE constraint failed/);
} finally {
db.close();
expect(db.isOpen()).toBe(false);
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/vite-repro/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<html>
<body>
<script type="module" src="./main.js"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions src/__tests__/vite-repro/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import sqlite3InitModule from '../../../src/index.js';

await sqlite3InitModule();
82 changes: 75 additions & 7 deletions src/__tests__/workers/sqlite3-sahpool.worker.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import sqlite3InitModule from '../../bin/sqlite3-bundler-friendly.mjs';

self.onmessage = async (e) => {
self.onmessage = async () => {
try {
const sqlite3 = await sqlite3InitModule();
const opfsSahPool = await sqlite3.installOpfsSAHPoolVfs();
const db = new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');
let db = new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');

try {
// 1. Basic CRUD
Expand All @@ -29,6 +29,15 @@ self.onmessage = async (e) => {
throw new Error('CRUD check failed');
}

db.close();

// Reopen to check persistence
db = new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');
const count = db.selectValue('SELECT count(*) FROM test');
if (count !== 2) {
throw new Error('Persistence check failed');
}

// 2. Joins
db.exec(
'CREATE TABLE orders (id INTEGER PRIMARY KEY, user_id INTEGER, product TEXT)',
Expand All @@ -42,30 +51,89 @@ self.onmessage = async (e) => {
rowMode: 'object',
callback: (row) => joinedRows.push(row),
});
if (joinedRows.length !== 2) throw new Error('Join check failed');
if (joinedRows.length !== 2) {
throw new Error('Join check failed');
}

// 3. CTE
const cteCount = db.selectValue(
'WITH RECURSIVE cnt(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 5) SELECT count(*) FROM cnt',
);
if (cteCount !== 5) throw new Error('CTE check failed');
if (cteCount !== 5) {
throw new Error('CTE check failed');
}

// 4. FTS5
db.exec('CREATE VIRTUAL TABLE docs USING fts5(content)');
db.exec("INSERT INTO docs (content) VALUES ('sqlite is great')");
const ftsResult = db.selectValue(
"SELECT content FROM docs WHERE docs MATCH 'sqlite'",
);
if (ftsResult !== 'sqlite is great') throw new Error('FTS5 check failed');
if (ftsResult !== 'sqlite is great') {
throw new Error('FTS5 check failed');
}

// 5. Math Functions
const cosResult = db.selectValue('SELECT cos(0)');
if (cosResult !== 1) throw new Error('Math functions check failed');
if (cosResult !== 1) {
throw new Error('Math functions check failed');
}

// 6. Percentile
db.exec('CREATE TABLE p(x); INSERT INTO p VALUES (1),(2),(3),(4),(5);');
const perc = db.selectValue('SELECT percentile(x, 50) FROM p');
if (perc !== 3) throw new Error('Percentile check failed');
if (perc !== 3) {
throw new Error('Percentile check failed');
}

// 7. pauseVfs and unpauseVfs
// Ensure it's not paused initially
if (opfsSahPool.isPaused()) {
throw new Error('VFS should not be paused initially');
}

db.close(); // Must close DB before pausing if it's the only one using it,
// or at least ensures no open file handles.
// Actually, pauseVfs throws if there are open file handles.

opfsSahPool.pauseVfs();
if (!opfsSahPool.isPaused()) {
throw new Error('VFS should be paused after pauseVfs()');
}

// Attempting to open a DB with a paused VFS should fail
try {
new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');
throw new Error('Opening DB should have failed while VFS is paused');
} catch (e) {
// Expected error
}

await opfsSahPool.unpauseVfs();
if (opfsSahPool.isPaused()) {
throw new Error('VFS should not be paused after unpauseVfs()');
}

// Test that pauseVfs() throws if there are open file handles
db = new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');
try {
opfsSahPool.pauseVfs();
throw new Error('pauseVfs should have failed with open DB handles');
} catch (e) {
if (!e.message.includes('Cannot pause VFS')) {
throw new Error(
'pauseVfs failed with unexpected error: ' + e.message,
);
}
}
db.close();

// Now it should work again
db = new opfsSahPool.OpfsSAHPoolDb('/test-sahpool-worker.sqlite3');
const count2 = db.selectValue('SELECT count(*) FROM test');
if (count2 !== 2) {
throw new Error('Persistence check after unpause failed');
}

self.postMessage({ type: 'success' });
} finally {
Expand Down
Loading