@fireproof/core 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/hooks/use-fireproof.js +135 -0
- package/package.json +1 -28
- package/scripts/keygen.js +3 -0
- package/test/block.js +65 -0
- package/test/clock.test.js +694 -0
- package/test/db-index.test.js +261 -0
- package/test/fireproof.test.js +491 -0
- package/test/fulltext.test.js +66 -0
- package/test/helpers.js +45 -0
- package/test/hydrator.test.js +81 -0
- package/test/listener.test.js +102 -0
- package/test/prolly.test.js +190 -0
- package/test/proofs.test.js +53 -0
- package/test/reproduce-fixture-bug.test.js +65 -0
- package/test/valet.test.js +59 -0
- package/tsconfig.json +104 -0
- package/webpack.config.js +8 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
/* global localStorage */
|
2
|
+
// @ts-ignore
|
3
|
+
import { useEffect, useState, createContext } from 'react'
|
4
|
+
import { Fireproof, Listener, Hydrator } from '../src/index'
|
5
|
+
|
6
|
+
// export interface FireproofCtxValue {
|
7
|
+
// addSubscriber: (label: String, fn: Function) => void
|
8
|
+
// database: Fireproof
|
9
|
+
// ready: boolean
|
10
|
+
// persist: () => void
|
11
|
+
// }
|
12
|
+
export const FireproofCtx = createContext({
|
13
|
+
addSubscriber: () => {},
|
14
|
+
database: null,
|
15
|
+
ready: false
|
16
|
+
})
|
17
|
+
|
18
|
+
const inboundSubscriberQueue = new Map()
|
19
|
+
|
20
|
+
let startedSetup = false
|
21
|
+
let database
|
22
|
+
let listener
|
23
|
+
const initializeDatabase = name => {
|
24
|
+
if (database) return
|
25
|
+
database = Fireproof.storage(name)
|
26
|
+
listener = new Listener(database)
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @function useFireproof
|
31
|
+
* React hook to initialize a Fireproof database, automatically saving and loading the clock.
|
32
|
+
* You might need to `import { nodePolyfills } from 'vite-plugin-node-polyfills'` in your vite.config.ts
|
33
|
+
* @param [defineDatabaseFn] Synchronous function that defines the database, run this before any async calls
|
34
|
+
* @param [setupDatabaseFn] Asynchronous function that sets up the database, run this to load fixture data etc
|
35
|
+
* @returns {FireproofCtxValue} { addSubscriber, database, ready }
|
36
|
+
*/
|
37
|
+
export function useFireproof (
|
38
|
+
defineDatabaseFn = () => {},
|
39
|
+
setupDatabaseFn = async () => {},
|
40
|
+
name
|
41
|
+
) {
|
42
|
+
const [ready, setReady] = useState(false)
|
43
|
+
initializeDatabase(name || 'useFireproof')
|
44
|
+
const localStorageKey = 'fp.' + database.name
|
45
|
+
|
46
|
+
const addSubscriber = (label, fn) => {
|
47
|
+
inboundSubscriberQueue.set(label, fn)
|
48
|
+
}
|
49
|
+
|
50
|
+
const listenerCallback = async event => {
|
51
|
+
localSet(localStorageKey, JSON.stringify(database))
|
52
|
+
if (event._external) return
|
53
|
+
for (const [, fn] of inboundSubscriberQueue) fn()
|
54
|
+
}
|
55
|
+
|
56
|
+
useEffect(() => {
|
57
|
+
const doSetup = async () => {
|
58
|
+
if (ready) return
|
59
|
+
if (startedSetup) return
|
60
|
+
startedSetup = true
|
61
|
+
defineDatabaseFn(database) // define indexes before querying them
|
62
|
+
console.log('Initializing database', database.name)
|
63
|
+
const fp = localGet(localStorageKey) // todo use db.name
|
64
|
+
if (fp) {
|
65
|
+
try {
|
66
|
+
const serialized = JSON.parse(fp)
|
67
|
+
// console.log('serialized', JSON.stringify(serialized.indexes.map(c => c.clock)))
|
68
|
+
console.log(`Loading previous database clock. (localStorage.removeItem('${localStorageKey}') to reset)`)
|
69
|
+
await Hydrator.fromJSON(serialized, database)
|
70
|
+
const changes = await database.changesSince()
|
71
|
+
if (changes.rows.length < 2) {
|
72
|
+
// console.log('Resetting database')
|
73
|
+
throw new Error('Resetting database')
|
74
|
+
}
|
75
|
+
} catch (e) {
|
76
|
+
console.error(`Error loading previous database clock. ${fp} Resetting.`, e)
|
77
|
+
await Hydrator.zoom(database, [])
|
78
|
+
await setupDatabaseFn(database)
|
79
|
+
localSet(localStorageKey, JSON.stringify(database))
|
80
|
+
}
|
81
|
+
} else {
|
82
|
+
await setupDatabaseFn(database)
|
83
|
+
localSet(localStorageKey, JSON.stringify(database))
|
84
|
+
}
|
85
|
+
setReady(true)
|
86
|
+
listener.on('*', listenerCallback)// hushed('*', listenerCallback, 250))
|
87
|
+
}
|
88
|
+
doSetup()
|
89
|
+
}, [ready])
|
90
|
+
|
91
|
+
return {
|
92
|
+
addSubscriber,
|
93
|
+
database,
|
94
|
+
ready,
|
95
|
+
persist: () => {
|
96
|
+
localSet(localStorageKey, JSON.stringify(database))
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
// const husherMap = new Map()
|
102
|
+
// const husher = (id, workFn, ms) => {
|
103
|
+
// if (!husherMap.has(id)) {
|
104
|
+
// const start = Date.now()
|
105
|
+
// husherMap.set(
|
106
|
+
// id,
|
107
|
+
// workFn().finally(() => setTimeout(() => husherMap.delete(id), ms - (Date.now() - start)))
|
108
|
+
// )
|
109
|
+
// }
|
110
|
+
// return husherMap.get(id)
|
111
|
+
// }
|
112
|
+
// const hushed =
|
113
|
+
// (id, workFn, ms) =>
|
114
|
+
// (...args) =>
|
115
|
+
// husher(id, () => workFn(...args), ms)
|
116
|
+
|
117
|
+
let storageSupported = false
|
118
|
+
try {
|
119
|
+
storageSupported = window.localStorage && true
|
120
|
+
} catch (e) {}
|
121
|
+
export function localGet (key) {
|
122
|
+
if (storageSupported) {
|
123
|
+
return localStorage && localStorage.getItem(key)
|
124
|
+
}
|
125
|
+
}
|
126
|
+
function localSet (key, value) {
|
127
|
+
if (storageSupported) {
|
128
|
+
return localStorage && localStorage.setItem(key, value)
|
129
|
+
}
|
130
|
+
}
|
131
|
+
// function localRemove(key) {
|
132
|
+
// if (storageSupported) {
|
133
|
+
// return localStorage && localStorage.removeItem(key)
|
134
|
+
// }
|
135
|
+
// }
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fireproof/core",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.6",
|
4
4
|
"description": "Realtime database for IPFS",
|
5
5
|
"main": "src/index.js",
|
6
6
|
"type": "module",
|
@@ -80,33 +80,6 @@
|
|
80
80
|
"url": "https://github.com/fireproof-storage/fireproof/issues"
|
81
81
|
},
|
82
82
|
"homepage": "https://fireproof.storage",
|
83
|
-
"exports": {
|
84
|
-
".": {
|
85
|
-
"types": "./dist/src/index.d.ts",
|
86
|
-
"import": "./src/index.js"
|
87
|
-
},
|
88
|
-
"./fireproof": {
|
89
|
-
"types": "./dist/src/fireproof.d.ts",
|
90
|
-
"import": "./src/fireproof.js"
|
91
|
-
},
|
92
|
-
"./hydrator": {
|
93
|
-
"types": "./dist/src/hydrator.d.ts",
|
94
|
-
"import": "./src/hydrator.js"
|
95
|
-
},
|
96
|
-
"./listener": {
|
97
|
-
"types": "./dist/src/listener.d.ts",
|
98
|
-
"import": "./src/listener.js"
|
99
|
-
},
|
100
|
-
"./db-index": {
|
101
|
-
"types": "./dist/src/db-index.d.ts",
|
102
|
-
"import": "./src/db-index.js"
|
103
|
-
}
|
104
|
-
},
|
105
|
-
"files": [
|
106
|
-
"src",
|
107
|
-
"dist",
|
108
|
-
"README.md"
|
109
|
-
],
|
110
83
|
"workspaces": [
|
111
84
|
"examples/todomvc"
|
112
85
|
]
|
package/test/block.js
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
import { parse } from 'multiformats/link'
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @typedef {{ cid: import('../src/link').AnyLink, bytes: Uint8Array }} AnyBlock
|
5
|
+
* @typedef {{ get: (link: import('../src/link').AnyLink) => Promise<AnyBlock | undefined> }} BlockFetcher
|
6
|
+
*/
|
7
|
+
|
8
|
+
/** @implements {BlockFetcher} */
|
9
|
+
export class MemoryBlockstore {
|
10
|
+
/** @type {Map<string, Uint8Array>} */
|
11
|
+
blocks = new Map()
|
12
|
+
|
13
|
+
/**
|
14
|
+
* @param {import('../src/link').AnyLink} cid
|
15
|
+
* @returns {Promise<AnyBlock | undefined>}
|
16
|
+
*/
|
17
|
+
async get (cid) {
|
18
|
+
const bytes = this.blocks.get(cid.toString())
|
19
|
+
if (!bytes) return
|
20
|
+
return { cid, bytes }
|
21
|
+
}
|
22
|
+
|
23
|
+
/**
|
24
|
+
* @param {import('../src/link').AnyLink} cid
|
25
|
+
* @param {Uint8Array} bytes
|
26
|
+
*/
|
27
|
+
async put (cid, bytes) {
|
28
|
+
// console.log('put', cid)
|
29
|
+
this.blocks.set(cid.toString(), bytes)
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @param {import('../src/link').AnyLink} cid
|
34
|
+
* @param {Uint8Array} bytes
|
35
|
+
*/
|
36
|
+
putSync (cid, bytes) {
|
37
|
+
this.blocks.set(cid.toString(), bytes)
|
38
|
+
}
|
39
|
+
|
40
|
+
* entries () {
|
41
|
+
for (const [str, bytes] of this.blocks) {
|
42
|
+
yield { cid: parse(str), bytes }
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
export class MultiBlockFetcher {
|
48
|
+
/** @type {BlockFetcher[]} */
|
49
|
+
fetchers
|
50
|
+
|
51
|
+
/** @param {BlockFetcher[]} fetchers */
|
52
|
+
constructor (...fetchers) {
|
53
|
+
this.fetchers = fetchers
|
54
|
+
}
|
55
|
+
|
56
|
+
/** @param {import('../src/link').AnyLink} link */
|
57
|
+
async get (link) {
|
58
|
+
for (const f of this.fetchers) {
|
59
|
+
const v = await f.get(link)
|
60
|
+
if (v) {
|
61
|
+
return v
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|