Skip to content

Commit 0b2cb00

Browse files
committed
[opfs] Move into browser module
1 parent 477f866 commit 0b2cb00

File tree

12 files changed

+165
-201
lines changed

12 files changed

+165
-201
lines changed

gulpfile.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ const ALL_MODULES = [
3131
'persisters/persister-file',
3232
'persisters/persister-indexed-db',
3333
'persisters/persister-libsql',
34-
'persisters/persister-opfs',
3534
'persisters/persister-partykit-client',
3635
'persisters/persister-partykit-server',
3736
'persisters/persister-pglite',

site/build.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,6 @@ const addApi = (docs: Docs): Docs =>
247247
.addApiFile('dist/@types/persisters/persister-file/index.d.ts')
248248
.addApiFile('dist/@types/persisters/persister-indexed-db/index.d.ts')
249249
.addApiFile('dist/@types/persisters/persister-libsql/index.d.ts')
250-
.addApiFile('dist/@types/persisters/persister-opfs/index.d.ts')
251250
.addApiFile('dist/@types/persisters/persister-partykit-client/index.d.ts')
252251
.addApiFile('dist/@types/persisters/persister-partykit-server/index.d.ts')
253252
.addApiFile('dist/@types/persisters/persister-pglite/index.d.ts')

src/@types/persisters/persister-browser/docs.js

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
/**
22
* The persister-browser module of the TinyBase project lets you save and load
3-
* Store data to and from browser storage.
3+
* Store data to and from browser storage, including the origin private file
4+
* system (OPFS).
45
*
5-
* Two entry points are provided, each of which returns a new Persister object
6+
* Three entry points are provided, each of which returns a new Persister object
67
* that can load and save a Store:
78
*
89
* - The createSessionPersister function returns a Persister that uses the
910
* browser's session storage.
1011
* - The createLocalPersister function returns a Persister that uses the
1112
* browser's local storage.
13+
* - The createOpfsPersister function returns a Persister that uses a file in
14+
* an origin private file system (OPFS).
1215
* @see Persistence guides
1316
* @packageDocumentation
1417
* @module persister-browser
@@ -95,6 +98,49 @@
9598
*/
9699
/// LocalPersister.getStorageName
97100
}
101+
/**
102+
* The OpfsPersister interface represents a Persister that lets you save and
103+
* load Store data to and from a file in an origin private file system (OPFS).
104+
*
105+
* You should use the createOpfsPersister function to create an OpfsPersister
106+
* object.
107+
*
108+
* It is a minor extension to the Persister interface and simply provides an
109+
* extra getHandle method for accessing the file the Store is being
110+
* persisted to.
111+
* @category Persister
112+
* @since v6.7.0
113+
*/
114+
/// OpfsPersister
115+
{
116+
/**
117+
* The getHandle method returns the handle of the file the Store is being
118+
* persisted to.
119+
* @returns The handle of the file.
120+
* @example
121+
* This example creates a Persister object against a newly-created Store and
122+
* then gets the file handle back out again.
123+
*
124+
* ```js
125+
* import {createStore} from 'tinybase';
126+
* import {createOpfsPersister} from 'tinybase/persisters/persister-browser';
127+
*
128+
* const opfs = await navigator.storage.getDirectory();
129+
* const handle = await opfs.getFileHandle('tinybase.json', {create: true});
130+
*
131+
* const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
132+
* const persister = createOpfsPersister(store, handle);
133+
*
134+
* console.log(persister.getHandle().name);
135+
* // -> 'tinybase.json'
136+
*
137+
* await persister.destroy();
138+
* ```
139+
* @category Getter
140+
* @since v6.7.0
141+
*/
142+
/// OpfsPersister.getHandle
143+
}
98144
/**
99145
* The createSessionPersister function creates a SessionPersister object that
100146
* can persist the Store to the browser's session storage.
@@ -171,3 +217,43 @@
171217
* @since v1.0.0
172218
*/
173219
/// createLocalPersister
220+
/**
221+
* The createOpfsPersister function creates an OpfsPersister object that can
222+
* persist the Store to a file in an origin private file system (OPFS).
223+
*
224+
* An OpfsPersister supports both regular Store and MergeableStore objects.
225+
*
226+
* As well as providing a reference to the Store to persist, you must provide a
227+
* `handle` parameter which identifies an existing OPFS file to persist it to.
228+
* @param store The Store or MergeableStore to persist.
229+
* @param handle The handle of an existing OPFS file to persist the Store to.
230+
* @param onIgnoredError An optional handler for the errors that the Persister
231+
* would otherwise ignore when trying to save or load data. This is suitable for
232+
* debugging persistence issues in a development environment.
233+
* @returns A reference to the new OpfsPersister object.
234+
* @example
235+
* This example creates an OpfsPersister object and persists the Store to a
236+
* local file.
237+
*
238+
* ```js
239+
* import {createStore} from 'tinybase';
240+
* import {createOpfsPersister} from 'tinybase/persisters/persister-browser';
241+
*
242+
* const opfs = await navigator.storage.getDirectory();
243+
* const handle = await opfs.getFileHandle('tinybase.json', {create: true});
244+
*
245+
* const store = createStore().setTables({pets: {fido: {species: 'dog'}}});
246+
* const persister = createOpfsPersister(store, handle);
247+
*
248+
* await persister.save();
249+
* // Store JSON will be saved to the file.
250+
*
251+
* await persister.load();
252+
* // Store JSON will be loaded from the file.
253+
*
254+
* await persister.destroy();
255+
* ```
256+
* @category Creation
257+
* @since v6.7.0
258+
*/
259+
/// createOpfsPersister

src/@types/persisters/persister-browser/index.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,17 @@ export function createLocalPersister(
3030
storageName: string,
3131
onIgnoredError?: (error: any) => void,
3232
): LocalPersister;
33+
34+
/// OpfsPersister
35+
export interface OpfsPersister
36+
extends Persister<Persists.StoreOrMergeableStore> {
37+
/// OpfsPersister.getHandle
38+
getHandle(): FileSystemFileHandle;
39+
}
40+
41+
/// createOpfsPersister
42+
export function createOpfsPersister(
43+
store: Store | MergeableStore,
44+
handle: FileSystemFileHandle,
45+
onIgnoredError?: (error: any) => void,
46+
): OpfsPersister;

src/@types/persisters/persister-browser/with-schemas/index.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ export interface LocalPersister<Schemas extends OptionalSchemas>
2020
getStorageName(): string;
2121
}
2222

23+
/// OpfsPersister
24+
export interface OpfsPersister<Schemas extends OptionalSchemas>
25+
extends Persister<Schemas, Persists.StoreOrMergeableStore> {
26+
/// OpfsPersister.getHandle
27+
getHandle(): string;
28+
}
29+
2330
/// createSessionPersister
2431
export function createSessionPersister<Schemas extends OptionalSchemas>(
2532
store: Store<Schemas> | MergeableStore<Schemas>,
@@ -33,3 +40,10 @@ export function createLocalPersister<Schemas extends OptionalSchemas>(
3340
storageName: string,
3441
onIgnoredError?: (error: any) => void,
3542
): LocalPersister<Schemas>;
43+
44+
/// createOpfsPersister
45+
export function createOpfsPersister<Schemas extends OptionalSchemas>(
46+
store: Store<Schemas> | MergeableStore<Schemas>,
47+
handle: FileSystemFileHandle,
48+
onIgnoredError?: (error: any) => void,
49+
): OpfsPersister<Schemas>;

src/@types/persisters/persister-opfs/docs.js

Lines changed: 0 additions & 93 deletions
This file was deleted.

src/@types/persisters/persister-opfs/index.d.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/@types/persisters/persister-opfs/with-schemas/index.d.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/persisters/persister-browser/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import type {
77
} from '../../@types/persisters/index.d.ts';
88
import type {
99
createLocalPersister as createLocalPersisterDecl,
10+
createOpfsPersister as createOpfsPersisterDecl,
1011
createSessionPersister as createSessionPersisterDecl,
1112
LocalPersister,
13+
OpfsPersister,
1214
SessionPersister,
1315
} from '../../@types/persisters/persister-browser/index.d.ts';
1416
import type {Store} from '../../@types/store/index.d.ts';
@@ -20,6 +22,11 @@ import {tryCatch, WINDOW} from '../../common/other.ts';
2022
import {createCustomPersister} from '../common/create.ts';
2123

2224
type StorageListener = (event: StorageEvent) => void;
25+
type FileSystemObserver = {
26+
observe: (handle: FileSystemFileHandle) => Promise<void>;
27+
disconnect: () => void;
28+
};
29+
2330
const STORAGE = 'storage';
2431

2532
const createStoragePersister = (
@@ -90,3 +97,44 @@ export const createSessionPersister = ((
9097
sessionStorage,
9198
onIgnoredError,
9299
) as SessionPersister) as typeof createSessionPersisterDecl;
100+
101+
export const createOpfsPersister = ((
102+
store: Store | MergeableStore,
103+
handle: FileSystemFileHandle,
104+
onIgnoredError?: (error: any) => void,
105+
): OpfsPersister => {
106+
const getPersisted = async (): Promise<
107+
PersistedContent<PersistsType.StoreOrMergeableStore>
108+
> => jsonParseWithUndefined(await (await handle.getFile()).text());
109+
110+
const setPersisted = async (
111+
getContent: () => PersistedContent<PersistsType.StoreOrMergeableStore>,
112+
): Promise<void> => {
113+
const writable = await handle.createWritable();
114+
await writable.write(jsonStringWithUndefined(getContent()));
115+
await writable.close();
116+
};
117+
118+
const addPersisterListener = async (
119+
listener: PersisterListener<PersistsType.StoreOrMergeableStore>,
120+
): Promise<FileSystemObserver> => {
121+
// @ts-expect-error FileSystemObserver is not yet typed
122+
const observer = new FileSystemObserver(() => listener());
123+
await observer.observe(handle);
124+
return observer;
125+
};
126+
127+
const delPersisterListener = (observer: FileSystemObserver) =>
128+
observer?.disconnect();
129+
130+
return createCustomPersister(
131+
store,
132+
getPersisted,
133+
setPersisted,
134+
addPersisterListener,
135+
delPersisterListener,
136+
onIgnoredError,
137+
3, // StoreOrMergeableStore,
138+
{getHandle: () => handle},
139+
) as OpfsPersister;
140+
}) as typeof createOpfsPersisterDecl;

0 commit comments

Comments
 (0)