db_factory.go raw

   1  //go:build js && wasm
   2  // +build js,wasm
   3  
   4  package idb
   5  
   6  import (
   7  	"context"
   8  	"errors"
   9  	"sync"
  10  	"syscall/js"
  11  
  12  	"github.com/hack-pad/safejs"
  13  )
  14  
  15  // Factory lets applications asynchronously access the indexed databases. A typical program will call Global() to access window.indexedDB.
  16  type Factory struct {
  17  	jsFactory safejs.Value
  18  }
  19  
  20  var (
  21  	global     *Factory
  22  	globalErr  error
  23  	globalOnce sync.Once
  24  )
  25  
  26  // Global returns the global IndexedDB instance.
  27  // Can be called multiple times, will always return the same result (or error if one occurs).
  28  func Global() *Factory {
  29  	globalOnce.Do(func() {
  30  		var jsFactory safejs.Value
  31  		jsFactory, globalErr = safejs.Global().Get("indexedDB")
  32  		if globalErr != nil {
  33  			return
  34  		}
  35  		var truthy bool
  36  		truthy, globalErr = jsFactory.Truthy()
  37  		if globalErr != nil {
  38  			return
  39  		}
  40  		if truthy {
  41  			global, globalErr = WrapFactory(safejs.Unsafe(jsFactory))
  42  		} else {
  43  			globalErr = errors.New("Global JS variable 'indexedDB' is not defined")
  44  		}
  45  	})
  46  	if globalErr != nil {
  47  		panic(globalErr)
  48  	}
  49  	return global
  50  }
  51  
  52  // WrapFactory wraps the given IDBFactory object
  53  func WrapFactory(jsFactory js.Value) (*Factory, error) {
  54  	return &Factory{
  55  		jsFactory: safejs.Safe(jsFactory),
  56  	}, nil
  57  }
  58  
  59  // Open requests to open a connection to a database.
  60  func (f *Factory) Open(upgradeCtx context.Context, name string, version uint, upgrader Upgrader) (*OpenDBRequest, error) {
  61  	args := []interface{}{name}
  62  	if version > 0 {
  63  		args = append(args, version)
  64  	}
  65  	reqValue, err := f.jsFactory.Call("open", args...)
  66  	if err != nil {
  67  		return nil, tryAsDOMException(err)
  68  	}
  69  	req := wrapRequest(nil, reqValue)
  70  	return newOpenDBRequest(upgradeCtx, req, upgrader)
  71  }
  72  
  73  // DeleteDatabase requests the deletion of a database.
  74  func (f *Factory) DeleteDatabase(name string) (*AckRequest, error) {
  75  	reqValue, err := f.jsFactory.Call("deleteDatabase", name)
  76  	if err != nil {
  77  		return nil, tryAsDOMException(err)
  78  	}
  79  	req := wrapRequest(nil, reqValue)
  80  	return newAckRequest(req), nil
  81  }
  82  
  83  // CompareKeys compares two keys and returns a result indicating which one is greater in value.
  84  func (f *Factory) CompareKeys(a, b js.Value) (int, error) {
  85  	compare, err := f.jsFactory.Call("cmp", a, b)
  86  	if err != nil {
  87  		return 0, tryAsDOMException(err)
  88  	}
  89  	return compare.Int()
  90  }
  91