sqlite / sqlite-wasm

SQLite Wasm conveniently wrapped as an ES Module.
520 stars 46 forks source link

Not able to pass the specified arguments to promiserFactory in order to use OFPS in a separate thread #86

Closed rubtobar closed 3 weeks ago

rubtobar commented 3 weeks ago

I'm triyng to implement sqlite-wasm database in my angular project, but i'm being unable to follow the guide.

As it says in https://sqlite.org/wasm/doc/trunk/api-worker1.md#method-open it should be possible to initialize the promiserFactory using some arguments.

As I underestand I should pass an object with the parameter "worker": worker: Worker or function A Worker instance which loads sqlite3-worker1.js or a functional equivalent. Note that the promiser factory replaces the worker.onmessage property. This config option may alternately be a function, in which case this function re-assigns this property with the result of calling that function, enabling delayed instantiation of a Worker.

But when I try to pass this Object to the promiserFactory it says that it only accepts an "InitOptions" object. Witch do not have the parameters shown in the documents.

This only lets me launch the promiserFactory without parameters, witch blocks me from using OPFS as the database persistent storage.

Here is my code:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { default as promiserFactory } from '@sqlite.org/sqlite-wasm';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent implements OnInit {
  title = 'dividendos-fontend';

  sqlite3Worker!: Worker;
  promiser: any;

  initializeSQLite = async () => {
    try {
      if (typeof Worker !== 'undefined') {
        console.log('Web Workers are supported in this environment.');
        this.sqlite3Worker = new Worker('sqlite3-worker1.js'); // or equivalent
      } else {
        console.log('Web Workers are not supported in this environment.');
      }

      console.log('Loading and initializing SQLite3 module...');

      const promiser = await promiserFactory(/* optional config */);

      console.log('Done initializing. Running demo...');

      const configResponse = await this.promiser('config-get', {});
      console.log('Running SQLite3 version', configResponse.result.version.libVersion);

      const openResponse = await this.promiser('open', {
        filename: 'file:mydb.sqlite3?vfs=opfs',
      });
      const { dbId } = openResponse;
      console.log(
        'OPFS is available, created persisted database at',
        openResponse.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
      );
      // Your SQLite code here.
    } catch (err) {
      if (!(err instanceof Error)) {
        console.log('Error:', err);
      }
    }
  };

  async ngOnInit() {

    await this.initializeSQLite();

  }

}

Thanks in advance and sorry if I made some misconception error.

sgbeal commented 3 weeks ago

Here is my code:

You've apparently confused the library-wide init routine with the promiser-specific init routine:

import { default as promiserFactory } from '@sqlite.org/sqlite-wasm';

That resolves to the library-wide init routine. That import will load the whole sqlite library into your thread and resolve to a function which initializes the library (necessarily asynchronously because some of the components, like the OPFS drivers, require async behavior). That is, incidentally, the preferred way to use the library, as the worker/promiser wrappers have severe limitations imposed by using postMessage() for remote-controlling sqlite (see the top of the docs linked below for details).

See this section of the docs for how to load the promiser factory. Be sure to read that whole (brief) section before attempting to use it, as it describes two different approaches to loading it (and the one you apparently want is not the first one).

Also, if you're using the Promiser API then you neither want nor need to explicitly load the Worker1 API, as the promiser API will load it on its own. The example code in the above link should demonstrate everything you need to load it via an ES6 module and the section above that one demonstrates how to use it.

rubtobar commented 3 weeks ago

First of all thanks for your help.

But I think i'm missing something.

Now I'm shure that i dont have to create a separate web worker, because the library will take care of doing it by itself.

So what I'm doing is import the library as the documentation says. With the import { default as promiserFactory } from '@sqlite.org/sqlite-wasm' I hope that i'm not mistaken, but if i have underestood correctly, that is the correct way of importing it.

Then i just try to await for the promiserFactory to initialize the SQlite DB. But I get an error in the browser saying: Ignoring inability to install OPFS sqlite3_vfs: The OPFS sqlite3_vfs cannot run in the main thread because it requires Atomics.wait().

So, I dont underestand how to create the promiserFactory.

Also i get an ERROR TypeError: this.promiser is not a function when trying to execute any promiser() function, so i assume that the return object from promiser = await promiserFactory() is not what i am trying to get.

I'm assuming that i'm doing something wrong with the imports, but i do not underestand what.

Here is what i have:

import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { default as promiserFactory } from "@sqlite.org/sqlite-wasm";

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent implements OnInit {
  title = 'dividendos-fontend';

  promiser!: any;

  initializeSQLite = async () => {

    console.log('Loading and initializing SQLite3 module...');

    this.promiser = await promiserFactory(/* optional config */);

    console.log('Done initializing. Running demo...');

    const configResponse = await this.promiser('config-get', {});
    console.log('Running SQLite3 version', configResponse.result.version.libVersion);

    const openResponse = await this.promiser('open', {
      filename: 'file:mydb.sqlite3?vfs=opfs',
    });
    const { dbId } = openResponse;
    console.log(
      'OPFS is available, created persisted database at',
      openResponse.result.filename.replace(/^file:(.*?)\?vfs=opfs$/, '$1'),
    );
    // Your SQLite code here.

  };

  async ngOnInit() {

    await this.initializeSQLite();

  }

}
rubtobar commented 3 weeks ago

I think i solved it.

I just had to put the files: sqlite3-bundler-friendly.mjs sqlite3-worker1-bundler-friendly.mjs sqlite3-worker1-promiser.mjs

inside "assets", insidethe angular "src" directory:

.
├── app
│   ├── app.component.css
│   ├── app.component.html
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   ├── app.config.ts
│   ├── app.routes.ts
│   └── sqlite.worker.ts
├── assets
│   ├── sqlite3-bundler-friendly.mjs
│   ├── sqlite3-worker1-bundler-friendly.mjs
│   └── sqlite3-worker1-promiser.mjs
├── favicon.ico
├── index.html
├── main.ts
└── styles.css

Then i had to change the tsconfig.json file to allow the compiler to import javascript from the typescript files. Fot that its just necessary to add the "allowJs" to true inside the "compilerOptions".

{
  ...
  "compilerOptions": {
    ...
    "allowJs": true,
  }

Then it's possible to create an import statement like this: import { default as promiserFactory } from "../assets/sqlite3-worker1-promiser.mjs";

After that, everything seems to load correctly, and the OPFS shows the new DB file created. No errors are shown in the initialization now.

Thanks for all the help and docs provided 😄