electric-sql / pglite

Lightweight WASM Postgres with real-time, reactive bindings.
https://pglite.dev
Apache License 2.0
9.46k stars 204 forks source link

Extensions in web worker not available even if specified? #309

Closed Bewinxed closed 2 months ago

Bewinxed commented 2 months ago

Hello! I'm trying to make extensions work with the WebWorker.

Here is my worker.js

import { PGlite } from '../node_modules/@electric-sql/pglite/dist/index.js'
import { worker } from '../node_modules/@electric-sql/pglite/dist/worker/index.js'
import { pg_trgm } from '../node_modules/@electric-sql/pglite/dist/contrib/pg_trgm.js'

worker({
  async init(options) {
    const pg = new PGlite('opfs-ahp://neotab/neotab.db', {...options, extensions: { ...options.extensions, pg_trgm }})
    // If you want run any specific setup code for the worker process, you can do it here.
    return pg
  },
})

console.log('Worker process started')

(I'm using the direct imports because for some reason the '@' imports don't work when importing this script from /public (i'm using vite).

However, with this query:

CREATE EXTENSION IF NOT EXISTS pg_trgm;

  CREATE TABLE IF NOT EXISTS "bookmarks" (
    "id" TEXT PRIMARY KEY NOT NULL,
  "title" TEXT NOT NULL,
  "url" TEXT NOT NULL,
  "snippet" TEXT,
  "created_at" TEXT NOT NULL,
  "tags" TEXT[] NOT NULL,
  "favicon" TEXT,
  "visits" NUMERIC NOT NULL DEFAULT 0
  );

 CREATE INDEX IF NOT EXISTS "bookmarks_snippet_idx" ON "bookmarks" USING gin (snippet gin_trgm_ops);
CREATE INDEX IF NOT EXISTS "bookmarks_title_idx" ON "bookmarks" USING gin (title gin_trgm_ops);
CREATE INDEX IF NOT EXISTS "bookmarks_url_idx" ON "bookmarks" USING gin (url gin_trgm_ops);
CREATE INDEX IF NOT EXISTS "bookmarks_tags_idx" ON "bookmarks" USING gin (tags);

I get Error: extension "pg_trgm" is not available

I tried to query available extensions, and i don't see trgm on there, Any clue?

Thank you!

samwillis commented 2 months ago

Hey @Bewinxed, thanks for the report.

I'm struggling to reproduce this. Could you take a look at the network tab on the dev tools and see if it shows pg_trgm.tar.gz being downloaded sucsesfully?

Should look like this:

image

(There is a double forward slash in there that I'm going to go and fix now, but doesn't seem to cause an issue)

Bewinxed commented 2 months ago

Ok so after a day of testing, I found out how to make this work (I'm developing an Extension, with Svelte + Vite).

WORKER SCRIPT (And this must go somewhere in ./src, NOT /public or it won't be compiled

import { PGlite } from '@electric-sql/pglite'
import { worker } from '@electric-sql/pglite/worker'
import { pg_trgm } from '@electric-sql/pglite/contrib/pg_trgm'

worker({
  async init(options) {
    const pg = new PGlite('opfs-ahp://neotab/neotab.db', {
      ...options,
      extensions: { pg_trgm },
    })
    // If you want run any specific setup code for the worker process, you can do it here.
    return pg
  },
})

console.log('Worker process started')

Import the worker with this (Other ways don't seem to work)

import PGWorker from './worker.js?worker'

export const pglite = new PGliteWorker(
  new PGWorker({
    name: 'neotab-worker',
  }),
  {
    dataDir: 'opfs-ahp://neotab/neotab.db',
    relaxedDurability: true,
    // extensions: { pg_trgm },
  },
)

Now creating the extension then making indexes work!, even after build.

Also, you must set worker.format = 'es' (Default is iife) in vite.config.ts

import { defineConfig } from 'vite'
import { crx } from '@crxjs/vite-plugin'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import path from 'node:path'
import sveltePreprocess from 'svelte-preprocess'
import manifest from './src/manifest'
import { join } from 'node:path'
import { buildSync } from 'esbuild'
export default defineConfig(({ mode }) => {
  const production = mode === 'production'

  return {
    build: {
      emptyOutDir: true,
      outDir: 'build',
      rollupOptions: {
        output: {
          chunkFileNames: 'assets/chunk-[hash].js',
        },
      },
    },
    worker: {
      format: 'es',
    },
    plugins: [
      crx({ manifest }),
      svelte({
        compilerOptions: {
          dev: !production,
        },
        preprocess: sveltePreprocess({
          typescript: true,
        }),
      }),
      {
        apply: 'build',
        enforce: 'pre',
        transformIndexHtml() {
          buildSync({
            minify: true,
            bundle: true,
            entryPoints: [join(process.cwd(), 'public', 'worker.js')],
            outfile: join(process.cwd(), 'build', 'worker.js'),
            external: ['@electric-sql/pglite'],
            format: 'esm',
          })
        },
      },
    ],
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'),
      },
    },
    server: {
      host: 'localhost',
      strictPort: true,
      port: 5173,
      hmr: {
        port: 5173,
      },
    },
    optimizeDeps: {
      exclude: ['@electric-sql/pglite', '@electric-sql/pglite/worker'],
    },
  }
})
Bewinxed commented 2 months ago

Now I need to figure out how to communicate with this via another service worker 🤔 Since the extension's background script is a service worker in an of itself.