Skip to content

Commit c1d28bc

Browse files
committed
IndexedDB fixes and rename
1 parent 319c542 commit c1d28bc

File tree

6 files changed

+83
-55
lines changed

6 files changed

+83
-55
lines changed

.github/workflows/publish.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Publish Package to npmjs
2+
on:
3+
release:
4+
types: [published]
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
steps:
9+
- uses: actions/checkout@v4
10+
# Setup .npmrc file to publish to npm
11+
- uses: actions/setup-node@v4
12+
with:
13+
node-version: '20.x'
14+
registry-url: 'https://registry.npmjs.org'
15+
scope: '@duinoapp'
16+
- run: yarn
17+
- run: yarn build
18+
- run: yarn publish --access public
19+
env:
20+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

src/adaptors/fsa-api/index.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
import { BaseAdaptor, BaseAdaptorOptions } from '../../base';
88
import { FileStat, PathMap } from '../../definitions';
99

10-
const INDEXDB_VERSION = 0;
11-
const INDEXDB_DEFAULT_DB = 'files-multitool';
12-
const INDEXDB_DEFAULT_STORE = '~~fsa-api~~';
10+
const INDEXEDDB_VERSION = 0;
11+
const INDEXEDDB_DEFAULT_DB = 'files-multitool';
12+
const INDEXEDDB_DEFAULT_STORE = '~~fsa-api~~';
1313

1414
export interface FSAAdaptorOptions extends BaseAdaptorOptions {
1515
db?: string;
@@ -129,7 +129,7 @@ export class FSAAdaptor extends BaseAdaptor {
129129

130130
async init(): Promise<void> {
131131
const db = await new Promise((resolve, reject) => {
132-
const request = window.indexedDB.open(this.options.db || INDEXDB_DEFAULT_DB, INDEXDB_VERSION);
132+
const request = window.indexedDB.open(this.options.db || INDEXEDDB_DEFAULT_DB, INDEXEDDB_VERSION);
133133
request.onerror = (event) => {
134134
reject(event);
135135
};
@@ -138,7 +138,7 @@ export class FSAAdaptor extends BaseAdaptor {
138138
};
139139
request.onupgradeneeded = (event) => {
140140
const db = (event.target as any).result as IDBDatabase;
141-
const store = db.createObjectStore(this.options.store || INDEXDB_DEFAULT_STORE, { keyPath: 'path' });
141+
const store = db.createObjectStore(this.options.store || INDEXEDDB_DEFAULT_STORE, { keyPath: 'path' });
142142

143143
store.createIndex('ref', 'ref', { unique: true });
144144

@@ -147,8 +147,8 @@ export class FSAAdaptor extends BaseAdaptor {
147147
}) as IDBDatabase;
148148

149149
const store = db
150-
.transaction(this.options.store || INDEXDB_DEFAULT_STORE, 'readwrite')
151-
.objectStore(this.options.store || INDEXDB_DEFAULT_STORE);
150+
.transaction(this.options.store || INDEXEDDB_DEFAULT_STORE, 'readwrite')
151+
.objectStore(this.options.store || INDEXEDDB_DEFAULT_STORE);
152152

153153
let handle = await this._getHandleFromDB(store);
154154

@@ -177,11 +177,13 @@ export class FSAAdaptor extends BaseAdaptor {
177177
this.root = handle;
178178

179179
db.close();
180+
return super.init();
180181
}
181182

182183
async destroy(): Promise<void> {
183184
this.root = null;
184185
this._purgeCache();
186+
return super.destroy();
185187
}
186188

187189
async _getParentHandle(path: string, create?: boolean): Promise<FileSystemDirectoryHandle> {
Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
import { BaseAdaptor, BaseAdaptorOptions } from '../../base';
22
import { FileStat, PathMap } from '../../definitions';
33

4-
const INDEXDB_VERSION = 1;
5-
// const INDEXDB_DEFAULT_DB = 'files-multitool';
6-
const INDEXDB_DEFAULT_STORE = 'files';
4+
const INDEXEDDB_VERSION = 1;
5+
// const INDEXEDDB_DEFAULT_DB = 'files-multitool';
6+
const INDEXEDDB_DEFAULT_STORE = 'files';
77

8-
export interface IndexDBAdaptorOptions extends BaseAdaptorOptions {
8+
export interface IndexedDBAdaptorOptions extends BaseAdaptorOptions {
99
db?: string;
1010
}
1111

12-
interface IndexDBFileStat extends FileStat {
12+
interface IndexedDBFileStat extends FileStat {
1313
content?: string;
1414
deletedAt?: Date;
1515
}
1616

17-
export class IndexDBAdaptor extends BaseAdaptor {
18-
declare options: IndexDBAdaptorOptions;
17+
const joinPath = (path: string, fileName: string) => {
18+
return [
19+
path.replace(/\/$/, ''),
20+
fileName.replace(/^\//, ''),
21+
].filter(v => v).join('/');
22+
}
23+
24+
export class IndexedDBAdaptor extends BaseAdaptor {
25+
declare options: IndexedDBAdaptorOptions;
1926
db = null as IDBDatabase | null;
2027
indexedDB = null as IDBFactory | null;
2128

@@ -30,7 +37,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
3037
async init(): Promise<void> {
3138
this.indexedDB = window.indexedDB;
3239
this.db = await new Promise((resolve, reject) => {
33-
const request = this.indexedDB!.open(this.ref, INDEXDB_VERSION);
40+
const request = this.indexedDB!.open(this.ref, INDEXEDDB_VERSION);
3441
request.onerror = (event) => {
3542
reject(event);
3643
};
@@ -39,7 +46,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
3946
};
4047
request.onupgradeneeded = (event) => {
4148
const db = (event.target as any).result as IDBDatabase;
42-
const store = db.createObjectStore(INDEXDB_DEFAULT_STORE, { keyPath: 'path' });
49+
const store = db.createObjectStore(INDEXEDDB_DEFAULT_STORE, { keyPath: 'path' });
4350

4451
store.createIndex('path', 'path', { unique: true });
4552
store.createIndex('isDirectory', 'isDirectory', { unique: false });
@@ -55,24 +62,24 @@ export class IndexDBAdaptor extends BaseAdaptor {
5562
}) as IDBDatabase;
5663

5764
this.db.addEventListener('close', () => {
65+
super.destroy();
5866
this.db = null;
5967
});
68+
69+
return super.init();
6070
}
6171

6272
destroy(): Promise<void> {
63-
return new Promise((resolve) => {
64-
if (this.db) {
65-
this.db.addEventListener('close', () => {
66-
resolve();
67-
});
68-
this.db.close();
69-
}
70-
});
73+
if (this.db) {
74+
this.db.close();
75+
this.db = null;
76+
}
77+
return super.destroy();
7178
}
7279

7380
// internal helper methods
7481

75-
_convertFileStat(stat: IndexDBFileStat): FileStat {
82+
_convertFileStat(stat: IndexedDBFileStat): FileStat {
7683
const clonedStat = { ...stat };
7784
delete clonedStat.content;
7885
delete clonedStat.deletedAt;
@@ -84,19 +91,19 @@ export class IndexDBAdaptor extends BaseAdaptor {
8491
throw new Error('Adaptor is not initialized.');
8592
}
8693
return this.db!
87-
.transaction(INDEXDB_DEFAULT_STORE, 'readwrite')
88-
.objectStore(INDEXDB_DEFAULT_STORE);
94+
.transaction(INDEXEDDB_DEFAULT_STORE, 'readwrite')
95+
.objectStore(INDEXEDDB_DEFAULT_STORE);
8996
}
9097

91-
_getItem(path: string): Promise<IndexDBFileStat | null> {
98+
_getItem(path: string): Promise<IndexedDBFileStat | null> {
9299
const store = this._getStore();
93100
const request = store.get(path);
94101
return new Promise((resolve, reject) => {
95102
request.onerror = (event) => {
96103
reject(request.error);
97104
};
98105
request.onsuccess = (event) => {
99-
resolve((request.result || null) as IndexDBFileStat | null);
106+
resolve((request.result || null) as IndexedDBFileStat | null);
100107
};
101108
});
102109
}
@@ -114,7 +121,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
114121
});
115122
}
116123

117-
async _putItem(stat: IndexDBFileStat): Promise<IndexDBFileStat> {
124+
async _putItem(stat: IndexedDBFileStat): Promise<IndexedDBFileStat> {
118125
let writeStat = stat;
119126
const existing = await this._getItem(stat.path);
120127
if (existing && existing.deletedAt) {
@@ -137,7 +144,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
137144
});
138145
}
139146

140-
_listItems(path: string): Promise<IndexDBFileStat[]> {
147+
_listItems(path: string): Promise<IndexedDBFileStat[]> {
141148
const store = this._getStore();
142149
const index = store.index('parentPath');
143150
const request = index.getAll(path);
@@ -146,7 +153,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
146153
reject(request.error);
147154
};
148155
request.onsuccess = (event) => {
149-
resolve((request.result || []) as IndexDBFileStat[]);
156+
resolve((request.result || []) as IndexedDBFileStat[]);
150157
};
151158
});
152159
}
@@ -210,7 +217,6 @@ export class IndexDBAdaptor extends BaseAdaptor {
210217
for (let i = 0; i < parts.length; i++) {
211218
const partPath = parts.slice(0, i + 1).join('/');
212219
const stat = await this._getItem(partPath);
213-
console.log('mkdir', path, stat);
214220
if (!stat || stat.deletedAt) {
215221
await this._putItem({
216222
path: partPath,
@@ -234,7 +240,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
234240
const items = await this._listItems(path);
235241
for (const item of items) {
236242
if (item.deletedAt) continue;
237-
const itemPath = `${path}/${item.path.split('/').pop()}`;
243+
const itemPath = joinPath(path, item.path.split('/').pop() as string);
238244
if (item.isDirectory) {
239245
await this.rmdir(itemPath);
240246
} else {
@@ -255,7 +261,7 @@ export class IndexDBAdaptor extends BaseAdaptor {
255261
const fileName = newFileName || stat.path.split('/').pop()!;
256262
await this._putItem({
257263
...stat,
258-
path: [targetPath, fileName].join('/'),
264+
path: joinPath(targetPath, fileName),
259265
parentPath: targetPath,
260266
});
261267
}
@@ -269,9 +275,10 @@ export class IndexDBAdaptor extends BaseAdaptor {
269275
await this.mkdir(newPath);
270276
for (const item of items) {
271277
if (item.deletedAt) continue;
272-
const itemPath = `${path}/${item.path.split('/').pop()}`;
278+
const itemPath = joinPath(path, item.path.split('/').pop() as string);
273279
if (item.isDirectory && recursive) {
274-
await this._copyDirectory(itemPath, `${newPath}/${item.path.split('/').pop()}`, recursive);
280+
const newItemPath = joinPath(newPath, item.path.split('/').pop() as string);
281+
await this._copyDirectory(itemPath, newItemPath, recursive);
275282
} else if (item.isFile) {
276283
await this._copyFile(itemPath, newPath);
277284
}

src/adaptors/memory/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export class MemoryAdaptor extends BaseAdaptor {
2424
}
2525

2626
async init(): Promise<void> {
27-
this.isInitialized = true;
27+
return super.init();
2828
}
2929

3030
async destroy(): Promise<void> {
31-
this.isInitialized = false;
31+
this.files = {};
32+
return super.destroy();
3233
}
3334

3435
_toStat(file: MemoryFile): FileStat {

src/base.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ export abstract class BaseAdaptor {
2121
get isInitialized(): boolean {
2222
return this._isInitialized;
2323
}
24-
set isInitialized(value: boolean) {
25-
this._isInitialized = value;
26-
}
2724

2825
/**
2926
* Check if the adaptor is supported in the current environment.
@@ -71,7 +68,7 @@ export abstract class BaseAdaptor {
7168
* @returns {Promise<void>}
7269
*/
7370
async init(): Promise<void> {
74-
this.isInitialized = true;
71+
this._isInitialized = true;
7572
}
7673

7774
/**
@@ -81,7 +78,7 @@ export abstract class BaseAdaptor {
8178
* It wont delete the files, just the connection to the file system/database.
8279
*/
8380
async destroy(): Promise<void> {
84-
this.isInitialized = false;
81+
this._isInitialized = false;
8582
}
8683

8784
/**

src/index.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import { FileStat, PathMap, PathDump } from './definitions';
33
import { BaseAdaptor, BaseAdaptorOptions } from './base';
44

55
import { FSAAdaptor, FSAAdaptorOptions } from './adaptors/fsa-api';
6-
import { IndexDBAdaptor, IndexDBAdaptorOptions } from './adaptors/indexdb';
6+
import { IndexedDBAdaptor, IndexedDBAdaptorOptions } from './adaptors/indexed-db';
77
import { MemoryAdaptor, MemoryAdaptorOptions } from './adaptors/memory';
88

99
export { FileStat, PathMap, PathDump };
1010

1111
export enum FilesMultitoolType {
1212
FSA_API = 'fsa-api',
13-
INDEXDB = 'indexdb',
13+
INDEXEDDB = 'indexed-db',
1414
MEMORY = 'memory',
1515
}
1616

@@ -36,7 +36,7 @@ export interface FilesMultitoolPrettyTypes {
3636
* @property {FileStat} [oldStat] - The old stats of the file or directory if it was renamed.
3737
* @property {'added' | 'deleted' | 'modified' | 'renamed'} action - The action that occurred.
3838
*/
39-
interface FilesMultitoolChangeEvent {
39+
export interface FilesMultitoolChangeEvent {
4040
path: string;
4141
stat: FileStat;
4242
oldPath?: string;
@@ -59,7 +59,7 @@ interface FilesMultitoolEvents {
5959
'paths-changed': (paths: string[], changes: FilesMultitoolChangeEvent[]) => void;
6060
}
6161

62-
export interface FilesMultitoolOptions extends BaseAdaptorOptions, FSAAdaptorOptions, IndexDBAdaptorOptions, MemoryAdaptorOptions {
62+
export interface FilesMultitoolOptions extends BaseAdaptorOptions, FSAAdaptorOptions, IndexedDBAdaptorOptions, MemoryAdaptorOptions {
6363
}
6464

6565

@@ -95,8 +95,8 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
9595
case FilesMultitoolType.FSA_API:
9696
this.adaptor = new FSAAdaptor(ref, options as FSAAdaptorOptions);
9797
break;
98-
case FilesMultitoolType.INDEXDB:
99-
this.adaptor = new IndexDBAdaptor(ref, options as IndexDBAdaptorOptions);
98+
case FilesMultitoolType.INDEXEDDB:
99+
this.adaptor = new IndexedDBAdaptor(ref, options as IndexedDBAdaptorOptions);
100100
break;
101101
case FilesMultitoolType.MEMORY:
102102
this.adaptor = new MemoryAdaptor(ref, options as MemoryAdaptorOptions);
@@ -147,8 +147,8 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
147147
switch (type) {
148148
case FilesMultitoolType.FSA_API:
149149
return FSAAdaptor.isSupported();
150-
case FilesMultitoolType.INDEXDB:
151-
return IndexDBAdaptor.isSupported();
150+
case FilesMultitoolType.INDEXEDDB:
151+
return IndexedDBAdaptor.isSupported();
152152
case FilesMultitoolType.MEMORY:
153153
return MemoryAdaptor.isSupported();
154154
default:
@@ -173,7 +173,7 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
173173
{
174174
technology: 'IndexedDB',
175175
text: 'Local Browser Storage',
176-
value: FilesMultitoolType.INDEXDB,
176+
value: FilesMultitoolType.INDEXEDDB,
177177
},
178178
...(includeMemory ? [{
179179
technology: 'Memory',
@@ -234,6 +234,7 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
234234
const map = {} as PathMap;
235235
const root = await this.adaptor.list(path);
236236
for (const [key, value] of Object.entries(root)) {
237+
if (key === path) continue;
237238
map[key] = value;
238239
if (value.isDirectory && recursive) {
239240
const children = await this.list(value.path, recursive);
@@ -279,7 +280,6 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
279280
if (stat?.isDirectory) throw new Error('Cannot write to a directory.');
280281
const parentPath = path.split('/').slice(0, -1).join('/').replace(/(^\/)|(\/$)/g, '');
281282
if (parentPath) await this.adaptor.mkdir(parentPath);
282-
console.log('writing file', path, parentPath, stat, await this.adaptor.stat(parentPath));
283283
if (typeof content === 'string') {
284284
content = Buffer.from(content, encoding);
285285
}
@@ -485,9 +485,10 @@ export default class FilesMultitool extends TypedEmitter<FilesMultitoolEvents>{
485485
if (sourceStat.isDirectory) sourceChildren = await this.list(source, true);
486486
await this.adaptor.move(source, destination);
487487
destinationStat = await this.adaptor.stat(destination);
488+
if (!destinationStat) throw new Error('Something went wrong. New destination was not found.');
488489
let destinationChildren: PathMap = {};
489490
if (sourceStat.isDirectory) destinationChildren = await this.list(destination, true);
490-
this._massEmit('renamed', destination, destinationStat!, destinationChildren, source, sourceStat, sourceChildren);
491+
this._massEmit('renamed', destination, destinationStat, destinationChildren, source, sourceStat, sourceChildren);
491492
}
492493

493494
/**

0 commit comments

Comments
 (0)