fs.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. //go:build wasm
  2. // +build wasm
  3. // Package indexeddb contains a WebAssembly compatible file system. Uses IndexedDB under the hood.
  4. package indexeddb
  5. import (
  6. "context"
  7. "time"
  8. "github.com/hack-pad/go-indexeddb/idb"
  9. "github.com/hack-pad/hackpadfs"
  10. "github.com/hack-pad/hackpadfs/keyvalue"
  11. "github.com/hack-pad/safejs"
  12. )
  13. const (
  14. fsVersion = 1
  15. contentsStore = "contents"
  16. infoStore = "info"
  17. parentKey = "Parent"
  18. )
  19. // FS is a browser-based file system, storing files and metadata inside IndexedDB.
  20. type FS struct {
  21. kv *keyvalue.FS
  22. db *idb.Database
  23. }
  24. // Options provides configuration options for a new FS.
  25. type Options struct {
  26. Factory *idb.Factory
  27. TransactionDurability idb.TransactionDurability
  28. }
  29. // NewFS returns a new FS.
  30. func NewFS(ctx context.Context, name string, options Options) (*FS, error) {
  31. if options.Factory == nil {
  32. options.Factory = idb.Global()
  33. }
  34. openRequest, err := options.Factory.Open(ctx, name, fsVersion, func(db *idb.Database, _, _ uint) error {
  35. _, err := db.CreateObjectStore(contentsStore, idb.ObjectStoreOptions{})
  36. if err != nil {
  37. return err
  38. }
  39. infos, err := db.CreateObjectStore(infoStore, idb.ObjectStoreOptions{})
  40. if err != nil {
  41. return err
  42. }
  43. jsParentKey, err := safejs.ValueOf(parentKey)
  44. if err != nil {
  45. return err
  46. }
  47. _, err = infos.CreateIndex(parentKey, safejs.Unsafe(jsParentKey), idb.IndexOptions{})
  48. return err
  49. })
  50. if err != nil {
  51. return nil, err
  52. }
  53. db, err := openRequest.Await(ctx)
  54. if err != nil {
  55. return nil, err
  56. }
  57. kv, err := keyvalue.NewFS(newStore(db, options))
  58. return &FS{
  59. kv: kv,
  60. db: db,
  61. }, err
  62. }
  63. // Clear dangerously destroys all data inside this FS. Use with caution.
  64. func (fs *FS) Clear(ctx context.Context) error {
  65. stores := []string{contentsStore, infoStore}
  66. txn, err := fs.db.Transaction(idb.TransactionReadWrite, stores[0], stores[1:]...)
  67. if err != nil {
  68. return err
  69. }
  70. for _, name := range stores {
  71. store, err := txn.ObjectStore(name)
  72. if err != nil {
  73. return err
  74. }
  75. _, err = store.Clear()
  76. if err != nil {
  77. return err
  78. }
  79. }
  80. err = txn.Commit()
  81. if err != nil {
  82. return err
  83. }
  84. err = txn.Await(ctx)
  85. if err != nil {
  86. return err
  87. }
  88. return fs.Mkdir(".", 0o666)
  89. }
  90. // Open implements hackpadfs.FS
  91. func (fs *FS) Open(name string) (hackpadfs.File, error) {
  92. return fs.kv.Open(name)
  93. }
  94. // OpenFile implements hackpadfs.OpenFileFS
  95. func (fs *FS) OpenFile(name string, flag int, perm hackpadfs.FileMode) (hackpadfs.File, error) {
  96. return fs.kv.OpenFile(name, flag, perm)
  97. }
  98. // Mkdir implements hackpadfs.MkdirFS
  99. func (fs *FS) Mkdir(name string, perm hackpadfs.FileMode) error {
  100. return fs.kv.Mkdir(name, perm)
  101. }
  102. // MkdirAll implements hackpadfs.MkdirAllFS
  103. func (fs *FS) MkdirAll(path string, perm hackpadfs.FileMode) error {
  104. return fs.kv.MkdirAll(path, perm)
  105. }
  106. // Remove implements hackpadfs.RemoveFS
  107. func (fs *FS) Remove(name string) error {
  108. return fs.kv.Remove(name)
  109. }
  110. // Rename implements hackpadfs.RenameFS
  111. func (fs *FS) Rename(oldname, newname string) error {
  112. return fs.kv.Rename(oldname, newname)
  113. }
  114. // Stat implements hackpadfs.StatFS
  115. func (fs *FS) Stat(name string) (hackpadfs.FileInfo, error) {
  116. return fs.kv.Stat(name)
  117. }
  118. // Chmod implements hackpadfs.ChmodFS
  119. func (fs *FS) Chmod(name string, mode hackpadfs.FileMode) error {
  120. return fs.kv.Chmod(name, mode)
  121. }
  122. // Chtimes implements hackpadfs.ChtimesFS
  123. func (fs *FS) Chtimes(name string, atime time.Time, mtime time.Time) error {
  124. return fs.kv.Chtimes(name, atime, mtime)
  125. }