@vanillaspa/sqlite-database 1.2.7 → 1.4.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.
Files changed (3) hide show
  1. package/index.js +88 -57
  2. package/package.json +3 -1
  3. package/sqliteWorker.js +16 -17
package/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
2
- import { dispatchEvent } from '@vanillaspa/event-bus';
2
+ import { addEventListener, dispatchEvent } from '@vanillaspa/event-bus';
3
3
 
4
4
  if (!window.Worker) throw new Error(`Your browser doesn't support web workers.`);
5
- export const name = "sqlite"; // module name
5
+
6
+ export const name = "sqlite";
7
+
6
8
  try {
7
9
  const sqlite3 = await sqlite3InitModule({ print: console.log, printErr: console.error });
8
10
  console.log('SQLite3 version:', sqlite3.version.libVersion);
@@ -11,11 +13,8 @@ try {
11
13
  }
12
14
 
13
15
  const workers = new Map();
14
-
15
- function getWorker(name = 'default') {
16
- const worker = workers.get(name);
17
- if (!worker) throw new Error(`No worker for "${name}"`);
18
- return worker;
16
+ export function getWorkers() {
17
+ return workers;
19
18
  }
20
19
 
21
20
  function initializeWorker(name) {
@@ -41,63 +40,95 @@ function enqueue(worker, payload) {
41
40
  })
42
41
  }
43
42
 
44
- // Public API
45
- export function createDB(name = 'default') {
46
- initializeWorker(name);
47
- return enqueue(getWorker(name), { action: 'createDB', name });
48
- }
49
-
50
- export async function deleteAndTerminateDB(name) {
51
- const worker = getWorker(name);
52
- await enqueue(worker, { action: 'closeDB' })
53
- const root = await navigator.storage.getDirectory();
54
- const fileHandle = await root.getFileHandle(`${name}.sqlite3`).catch(() => null);
55
- if (fileHandle) await fileHandle.remove();
56
- worker.terminate();
57
- workers.delete(name);
43
+ function fire(type, detail, context) {
44
+ dispatchEvent(new CustomEvent(type, { detail: { ...detail, target: context } }));
58
45
  }
46
+ // event-bus listener
47
+ addEventListener('sqlite:createDB', async (event) => {
48
+ const { name, target } = event.datail;
49
+ try {
50
+ initializeWorker(name);
51
+ const result = await enqueue(workers.get(name), { action: 'createDB', name });
52
+ fire('sqlite:ready', { result, name }, target);
53
+ } catch (error) {
54
+ fire('sqlite:error', { error: error.message, action: 'createDB' }, target);
55
+ }
56
+ }, import.meta);
59
57
 
60
- export function downloadDB(name = 'default') {
61
- enqueue(getWorker(name), { action: 'downloadDB' }).then(blob => {
62
- dispatchEvent(new CustomEvent('sqlite:download', { detail: { blob, name } }))
63
- })
64
- }
58
+ addEventListener('sqlite:query', async (event) => {
59
+ const { sql, name, target } = event.detail;
60
+ try {
61
+ const result = await enqueue(workers.get(name), { action: "executeQuery", sql });
62
+ fire('sqlite:result', { result, sql }, context);
63
+ } catch (error) {
64
+ fire('sqlite:error', { error: error.message, action: 'executeQuery', sql }, target);
65
+ }
66
+ }, import.meta);
65
67
 
66
- export function executeQuery(sql, name = 'default') {
67
- return enqueue(getWorker(name), { action: "executeQuery", sql });
68
- }
68
+ addEventListener('sqlite:statement', async (event) => {
69
+ const { sql, values, name, target } = event.detail;
70
+ try {
71
+ const result = await enqueue(workers.get(name), { action: "prepareStatement", sql, values });
72
+ fire('sqlite:result', { result, sql }, target);
73
+ } catch (error) {
74
+ fire('sqlite:error', { error: error.message, action: 'prepareStatement', sql }, target);
75
+ }
76
+ }, import.meta)
69
77
 
70
- export function executeStatement({ sql, values, name = "default" }) {
71
- return enqueue(getWorker(name), { action: "prepareStatement", sql, values });
72
- }
78
+ addEventListener('sqlite:upload', async (event) => {
79
+ const { fileName, arrayBuffer, target } = event.detail;
80
+ try {
81
+ const [name, extension] = fileName.split(".");
82
+ if (!['sqlite', 'sqlite3'].includes(extension)) {
83
+ throw new Error(`UnsupportedError: Unsupported extension ".${extension}"`);
84
+ }
85
+ if (!workers.has(name)) initializeWorker(name);
86
+ const result = await enqueue(workers.get(name), { action: 'uploadDB', name, arrayBuffer });
87
+ fire('sqlite:upload', { result, name }, target)
88
+ } catch (error) {
89
+ fire('sqlite:error', { error: error.message, action: 'uploadDB' }, target);
90
+ }
91
+ }, import.meta);
73
92
 
74
- export function uploadDB(fileName, arrayBuffer) {
75
- const [name, extension] = fileName.split(".");
76
- if (!['sqlite', 'sqlite3'].includes(extension)) {
77
- throw new Error(`UnsupportedError: Unsupported extension ".${extension}"`);
93
+ addEventListener('sqlite:download', async (event) => {
94
+ const { name, target } = event.detail;
95
+ try {
96
+ const blob = await enqueue(workers.get(name), { action: 'downloadDB' });
97
+ const url = URL.createObjectURL(blob);
98
+ const a = document.createElement('a');
99
+ a.href = url;
100
+ a.download = `${name}.sqlite3`;
101
+ a.click();
102
+ URL.revokeObjectURL(url)
103
+ fire('sqlite:downloaded', { name }, target);
104
+ } catch (error) {
105
+ fire('sqlite:error', { error: error.message, action: 'downloadDB' }, target);
78
106
  }
79
- if (!workers.has(name)) initializeWorker(name);
80
- return enqueue(workers.get(name), { action: 'uploadDB', name, arrayBuffer });
81
- }
107
+ }, import.meta);
82
108
 
83
- export function terminate(name = 'default') {
84
- const worker = workers.get(name);
85
- if (worker) {
86
- worker.terminate();
109
+ addEventListener('sqlite:closeDB', async (event) => {
110
+ const { name, target } = event.detail;
111
+ try {
112
+ await enqueue(workers.get(name), { action: 'closeDB' });
113
+ workers.get(name).terminate();
87
114
  workers.delete(name);
115
+ fire('sqlite:closed', { name }, target);
116
+ } catch (error) {
117
+ fire('sqlite:error', { error: error.message, action: 'closeDB' }, target);
88
118
  }
89
- }
119
+ }, import.meta);
90
120
 
91
- export function getWorkers() {
92
- return workers;
93
- }
94
-
95
- // addEventListener('sqlite:download', (event) => {
96
- // const { blob, name } = event.detail;
97
- // const url = URL.createObjectURL(blob);
98
- // const a = document.createElement('a');
99
- // a.href = url;
100
- // a.download = `${name}.sqlite3`;
101
- // a.click();
102
- // URL.revokeObjectURL(url);
103
- // });
121
+ addEventListener('sqlite:deleteDB', async (event) => {
122
+ const { name, target } = event.detail;
123
+ try {
124
+ await enqueue(workers.get(name), { action: 'closeDB' });
125
+ const root = await navigator.storage.getDirectory();
126
+ const fileHandle = await root.getFileHandle(`${name}.sqlite3`).catch(() => null);
127
+ if (fileHandle) await fileHandle.remove();
128
+ workers.get(name).terminate();
129
+ workers.delete(name);
130
+ fire('sqlite:deleted', { name }, target);
131
+ } catch (error) {
132
+ fire('sqlite:error', { error: error.message, action: 'deleteDB' }, target);
133
+ }
134
+ }, import.meta);
package/package.json CHANGED
@@ -10,9 +10,11 @@
10
10
  },
11
11
  "homepage": "https://github.com/vanillaspa/sqlite-database#readme",
12
12
  "keywords": [
13
+ "BroadcastChannel",
13
14
  "Database",
14
15
  "EventBus",
15
16
  "JavaScript",
17
+ "MessageChannel",
16
18
  "OPFS",
17
19
  "SPA",
18
20
  "SQLite",
@@ -30,5 +32,5 @@
30
32
  "url": "git+https://github.com/vanillaspa/sqlite-database.git"
31
33
  },
32
34
  "type": "module",
33
- "version": "1.2.7"
35
+ "version": "1.4.0"
34
36
  }
package/sqliteWorker.js CHANGED
@@ -2,7 +2,6 @@ import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
2
2
 
3
3
  let db = null;
4
4
  let sqlite3 = null;
5
- let port;
6
5
 
7
6
  async function getInstance() {
8
7
  if (!sqlite3) {
@@ -11,17 +10,17 @@ async function getInstance() {
11
10
  return sqlite3;
12
11
  }
13
12
 
14
- function reply(result) {
13
+ function reply(port, result) {
15
14
  port.postMessage({ type: 'application/json', result });
16
15
  port.close();
17
16
  }
18
17
 
19
- function replyError(message) {
18
+ function replyError(port, message) {
20
19
  port.postMessage({ type: 'error', message });
21
20
  port.close();
22
21
  }
23
22
 
24
- function handleSQLiteError(sql, e) {
23
+ function handleSQLiteError(port, sql, e) {
25
24
  if (e.message.includes('SQLITE_CANTOPEN')) {
26
25
  console.info("Info: No SQLite database available. Upload a new database or reload the page.");
27
26
  } else if (e.message.includes('SQLITE_CONSTRAINT_UNIQUE')) {
@@ -34,15 +33,15 @@ function handleSQLiteError(sql, e) {
34
33
 
35
34
  onmessage = async function ({ data, ports }) {
36
35
  const { action } = data;
37
- port = ports[0] ?? null;
36
+ const port = ports[0] ?? null;
38
37
 
39
38
  switch (action) {
40
39
  case 'closeDB': {
41
40
  try {
42
41
  closeDB();
43
- reply(null);
42
+ reply(port, null);
44
43
  } catch (e) {
45
- replyError(e.message);
44
+ replyError(port, e.message);
46
45
  }
47
46
  break;
48
47
  }
@@ -51,9 +50,9 @@ onmessage = async function ({ data, ports }) {
51
50
  try {
52
51
  const { newDB, message } = await createDatabase(name)
53
52
  db = newDB;
54
- reply(message);
53
+ reply(port, message);
55
54
  } catch (e) {
56
- replyError(e.message)
55
+ replyError(port, e.message)
57
56
  }
58
57
  break;
59
58
  }
@@ -61,9 +60,9 @@ onmessage = async function ({ data, ports }) {
61
60
  try {
62
61
  const byteArray = sqlite3.capi.sqlite3_js_db_export(db);
63
62
  const blob = new Blob([byteArray.buffer], { type: "application/vnd.sqlite3" });
64
- reply(blob);
63
+ reply(port, blob);
65
64
  } catch (e) {
66
- replyError(e.message);
65
+ replyError(port.e.message);
67
66
  }
68
67
  break;
69
68
  }
@@ -71,9 +70,9 @@ onmessage = async function ({ data, ports }) {
71
70
  const { sql } = data;
72
71
  try {
73
72
  const result = db.exec({ sql: sql.sql, returnValue: "resultRows" });
74
- reply(result);
73
+ reply(port, result);
75
74
  } catch (e) {
76
- handleSQLiteError(sql, e)
75
+ handleSQLiteError(port, sql, e)
77
76
  }
78
77
  break;
79
78
  }
@@ -89,9 +88,9 @@ onmessage = async function ({ data, ports }) {
89
88
  const row = stmt.get([]);
90
89
  result.push(Object.fromEntries(columns.map((columnName, index) => [columnName, row[index]])));
91
90
  }
92
- reply(result);
91
+ reply(port, result);
93
92
  } catch (e) {
94
- handleSQLiteError(sql, e);
93
+ handleSQLiteError(port, sql, e);
95
94
  } finally {
96
95
  stmt?.finalize();
97
96
  }
@@ -101,9 +100,9 @@ onmessage = async function ({ data, ports }) {
101
100
  const { name, arrayBuffer } = data;
102
101
  try {
103
102
  const message = await uploadDatabase(name, arrayBuffer)
104
- reply(message);
103
+ reply(port, message);
105
104
  } catch (e) {
106
- replyError(e.message);
105
+ replyError(port, e.message);
107
106
  }
108
107
  break;
109
108
  }