Browse Source

Init

master v0.1.0
Ezerous 4 years ago
commit
13e149dc02
  1. 2
      .gitattributes
  2. 15
      .gitignore
  3. 9
      .npmignore
  4. 21
      LICENSE
  5. 7
      README.md
  6. 17
      package.json
  7. 52
      src/Breeze.js
  8. 3
      src/breezeStatus/breezeActions.js
  9. 20
      src/breezeStatus/breezeStatusReducer.js
  10. 37
      src/breezeStatus/breezeStatusSaga.js
  11. 45
      src/index.js
  12. 3
      src/ipfs/ipfsActions.js
  13. 19
      src/ipfs/ipfsReducer.js
  14. 26
      src/ipfs/ipfsSaga.js
  15. 11
      src/misc/defaultOptions.js
  16. 8
      src/misc/mergeUtils.js
  17. 12
      src/orbit/constants.js
  18. 32
      src/orbit/orbitActions.js
  19. 18
      src/orbit/orbitMiddleware.js
  20. 54
      src/orbit/orbitReducer.js
  21. 96
      src/orbit/orbitSaga.js
  22. 61
      src/orbit/orbitStatusSaga.js
  23. 25
      src/orbit/orbitUtils.js
  24. 6763
      yarn.lock

2
.gitattributes

@ -0,0 +1,2 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto eol=lf

15
.gitignore

@ -0,0 +1,15 @@
# Node
/node_modules
# IDE
.DS_Store
.idea
# Logs
/log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# npm
*.tgz

9
.npmignore

@ -0,0 +1,9 @@
# Jetbrains
.idea
# Git
.gitattributes
# Package managers
yarn.lock
*.tgz

21
LICENSE

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Ezerous
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
README.md

@ -0,0 +1,7 @@
# @ezerous/breeze
[![Version](https://img.shields.io/npm/v/@ezerous/breeze.svg)](https://www.npmjs.com/package/@ezerous/breeze)
[![Dependencies](https://img.shields.io/david/Ezerous/breeze.svg)](https://david-dm.org/Ezerous/breeze)
[![License](https://img.shields.io/npm/l/@ezerous/breeze.svg)](https://www.npmjs.com/package/@ezerous/breeze)
A reactive data-store for [OrbitDB](https://github.com/orbitdb/orbit-db).

17
package.json

@ -0,0 +1,17 @@
{
"name": "@ezerous/breeze",
"version": "0.1.0",
"description": "A reactive data-store for OrbitDB.",
"license": "MIT",
"author": "Ezerous <ezerous@gmail.com>",
"main": "src/index.js",
"repository": "github:Ezerous/breeze",
"dependencies": {
"deepmerge": "4.2.2",
"ipfs": "0.50.1",
"is-plain-object": "4.1.1",
"orbit-db": "0.25.1",
"orbit-db-identity-provider": "0.3.1",
"redux-saga": "1.1.3"
}
}

52
src/Breeze.js

@ -0,0 +1,52 @@
import { BREEZE_INITIALIZING } from './breezeStatus/breezeActions'
import defaultOptions from "./misc/defaultOptions";
import merge from './misc/mergeUtils'
import {createOrbitDatabase, orbitInit} from "./orbit/orbitActions";
// Load as promise so that async Breeze initialization can still resolve
const isEnvReadyPromise = new Promise((resolve) => {
const hasWindow = typeof window !== 'undefined'
const hasDocument = typeof document !== 'undefined'
if (hasWindow)
return window.addEventListener('load', resolve)
// Resolve in any case if we missed the load event and the document is already loaded
if (hasDocument && document.readyState === `complete`) {
return resolve()
}
})
class Breeze {
constructor (breezeOptions, store) {
const options = merge(defaultOptions, breezeOptions)
this.store = store;
this.web3 = options.web3;
this.ipfs = {} // To be initialized in ipfsSaga
this.orbit = {} // To be initialized in orbitSaga
this.orbitDatabases = {};
this.ipfsOptions = options.ipfs;
this.orbitOptions = options.orbit;
// Wait for window load event in case of injected web3.
isEnvReadyPromise.then(() => {
// Begin Breeze initialization.
this.store.dispatch({
type: BREEZE_INITIALIZING,
breeze: this
})
})
}
initOrbit(id) {
this.store.dispatch(orbitInit (this, id));
}
// db = {name, type}
createOrbitDatabase (db){
this.store.dispatch(createOrbitDatabase (this.orbit, db));
}
}
export default Breeze

3
src/breezeStatus/breezeActions.js

@ -0,0 +1,3 @@
export const BREEZE_INITIALIZING = 'BREEZE_INITIALIZING';
export const BREEZE_INITIALIZED = 'BREEZE_INITIALIZED';
export const BREEZE_FAILED = 'BREEZE_FAILED';

20
src/breezeStatus/breezeStatusReducer.js

@ -0,0 +1,20 @@
import * as BreezeActions from './breezeActions'
const initialState = {
initialized: false
}
const breezeStatusReducer = (state = initialState, action) => {
/*
* Breeze Status
*/
if (action.type === BreezeActions.BREEZE_INITIALIZED) {
return {
...state,
initialized: true
}
}
return state
}
export default breezeStatusReducer

37
src/breezeStatus/breezeStatusSaga.js

@ -0,0 +1,37 @@
import { call, put, takeLatest } from 'redux-saga/effects'
import * as BreezeActions from './breezeActions';
import { initializeIPFS } from '../ipfs/ipfsSaga';
import { addOrbitIdentityProvider } from '../orbit/orbitSaga';
const LOGGING_PREFIX = 'breezeStatusSaga: ';
export function * initializeBreeze (action) {
try {
const { breeze } = action;
// Initialize IPFS
const ipfs = yield call(initializeIPFS, breeze.ipfsOptions);
if(!ipfs)
throw new Error('IPFS initialization error');
breeze.ipfs = ipfs;
// If given, add custom Orbit Identity Provider
if(breeze.orbitOptions.identityProvider)
yield call(addOrbitIdentityProvider, breeze.orbitOptions.identityProvider);
yield put({ type: BreezeActions.BREEZE_INITIALIZED, breeze });
} catch (error) {
yield put({ type: BreezeActions.BREEZE_FAILED, error });
console.error(LOGGING_PREFIX + 'Initialization error:');
console.error(error);
}
}
function * breezeStatusSaga () {
yield takeLatest(BreezeActions.BREEZE_INITIALIZING, initializeBreeze)
}
export default breezeStatusSaga

45
src/index.js

@ -0,0 +1,45 @@
import Breeze from './Breeze.js'
import breezeStatusReducer from './breezeStatus/breezeStatusReducer';
import ipfsReducer from "./ipfs/ipfsReducer";
import orbitReducer from "./orbit/orbitReducer";
import breezeStatusSaga from './breezeStatus/breezeStatusSaga';
import orbitSaga from "./orbit/orbitSaga";
import * as BreezeActions from './breezeStatus/breezeActions'
import * as OrbitActions from './orbit/orbitActions'
import * as orbitTypes from './orbit/constants'
import orbitMiddleware from "./orbit/orbitMiddleware";
import orbitStatusSaga from "./orbit/orbitStatusSaga";
const breezeReducers = {
breezeStatus: breezeStatusReducer,
ipfs: ipfsReducer,
orbit: orbitReducer
}
const breezeMiddlewares = [
orbitMiddleware
]
const breezeSagas = [
breezeStatusSaga,
orbitSaga,
orbitStatusSaga
]
const breezeActions = {
breeze: BreezeActions,
orbit: OrbitActions
}
export {
Breeze,
breezeActions,
breezeReducers,
breezeMiddlewares,
breezeSagas,
orbitTypes
}

3
src/ipfs/ipfsActions.js

@ -0,0 +1,3 @@
export const IPFS_INITIALIZING = 'IPFS_INITIALIZING';
export const IPFS_INITIALIZED = 'IPFS_INITIALIZED';
export const IPFS_FAILED = 'IPFS_FAILED';

19
src/ipfs/ipfsReducer.js

@ -0,0 +1,19 @@
import { IPFS_INITIALIZED } from "./ipfsActions";
const initialState = {
initialized: false,
};
const ipfsReducer = (state = initialState, action) => {
switch (action.type) {
case IPFS_INITIALIZED:
return {
...state,
initialized: true,
};
default:
return state;
}
};
export default ipfsReducer;

26
src/ipfs/ipfsSaga.js

@ -0,0 +1,26 @@
import { call, put } from 'redux-saga/effects'
import IPFS from 'ipfs';
import * as IpfsActions from "./ipfsActions";
const LOGGING_PREFIX = 'ipfsSaga: ';
/*
* Initialization
*/
export function * initializeIPFS (ipfsOptions) {
try {
yield put({ type: IpfsActions.IPFS_INITIALIZING });
// Initialize IPFS
const ipfs = yield call(IPFS.create, ipfsOptions);
yield put({ type: IpfsActions.IPFS_INITIALIZED });
return ipfs;
} catch (error) {
yield put({ type: IpfsActions.IPFS_FAILED, error });
console.error(LOGGING_PREFIX + 'IPFS Initialization error:');
console.error(error);
}
}

11
src/misc/defaultOptions.js

@ -0,0 +1,11 @@
const defaultOptions = {
// OrbitDB uses Pubsub which is an experimental feature and needs to be turned on manually.
EXPERIMENTAL: {
pubsub: true,
},
orbit: {
databases: []
}
}
export default defaultOptions

8
src/misc/mergeUtils.js

@ -0,0 +1,8 @@
const merge = require('deepmerge');
import isPlainObject from 'is-plain-object';
export default function (defaultOptions, customOptions) {
return merge(defaultOptions, customOptions, {
isMergeableObject: isPlainObject
})
}

12
src/orbit/constants.js

@ -0,0 +1,12 @@
// All valid OrbitDB types
export const ORBIT_TYPE_LOG = 'log';
export const ORBIT_TYPE_FEED = 'feed';
export const ORBIT_TYPE_KEYVALUE = 'keyvalue';
export const ORBIT_TYPE_DOCS = 'docs';
export const ORBIT_TYPE_COUNTER = 'counter';
// OrbitDB statuses
export const DB_STATUS_INIT = 'init';
export const DB_STATUS_READY = 'ready';
export const DB_STATUS_REPLICATING = 'replicating';
export const DB_STATUS_REPLICATED = 'replicated';

32
src/orbit/orbitActions.js

@ -0,0 +1,32 @@
export const ORBIT_INITIALIZING = 'ORBIT_INITIALIZING';
export const ORBIT_INITIALIZED = 'ORBIT_INITIALIZED';
export const ORBIT_INIT_FAILED = 'ORBIT_INIT_FAILED';
export const ORBIT_IDENTITY_PROVIDER_ADD = 'ORBIT_IDENTITY_PROVIDER_ADD';
export const ORBIT_IDENTITY_PROVIDER_ADDED = 'ORBIT_IDENTITY_PROVIDER_ADDED';
export const ORBIT_IDENTITY_PROVIDER_FAILED = 'ORBIT_IDENTITY_PROVIDER_FAILED';
export const ORBIT_DATABASE_CREATING = 'ORBIT_DATABASE_CREATING';
export const ORBIT_DATABASE_CREATED = 'ORBIT_DATABASE_CREATED';
export const ORBIT_DATABASE_FAILED = 'ORBIT_DATABASE_FAILED';
export const ORBIT_DATABASE_LISTEN = 'ORBIT_DATABASE_LISTEN';
// Database Events
export const ORBIT_DATABASE_READY = 'ORBIT_DATABASE_READY';
export const ORBIT_DATABASE_REPLICATING = 'ORBIT_DATABASE_REPLICATING';
export const ORBIT_DATABASE_REPLICATED = 'ORBIT_DATABASE_REPLICATED';
export function orbitInit (breeze, id) {
return {
type: ORBIT_INITIALIZING,
breeze,
id
}
}
export function createOrbitDatabase (orbit, db) {
return {
type: ORBIT_DATABASE_CREATING,
orbit,
db
}
}

18
src/orbit/orbitMiddleware.js

@ -0,0 +1,18 @@
import {ORBIT_DATABASE_CREATED} from "./orbitActions";
import {BREEZE_INITIALIZED} from "../breezeStatus/breezeActions";
export const orbitMiddleware = breezeInstance => () => next => action => {
const { type } = action
if (type === BREEZE_INITIALIZED)
breezeInstance = action.breeze
if (type === ORBIT_DATABASE_CREATED) {
const { database } = action;
breezeInstance.orbitDatabases[database.id] = database;
}
return next(action);
}
const initializedMiddleware = orbitMiddleware(undefined)
export default initializedMiddleware

54
src/orbit/orbitReducer.js

@ -0,0 +1,54 @@
import {
ORBIT_DATABASE_CREATED,
ORBIT_DATABASE_READY,
ORBIT_DATABASE_REPLICATED,
ORBIT_DATABASE_REPLICATING,
ORBIT_INITIALIZED
} from "./orbitActions";
import {
DB_STATUS_INIT,
DB_STATUS_READY,
DB_STATUS_REPLICATED,
DB_STATUS_REPLICATING
} from "./constants";
const initialState = {
initialized: false,
databases: {}
};
const orbitReducer = (state = initialState, action) => {
switch (action.type) {
case ORBIT_INITIALIZED:
return {
...state,
initialized: true,
};
case ORBIT_DATABASE_CREATED:
return newDatabasesStatus(state, action, DB_STATUS_INIT);
case ORBIT_DATABASE_READY:
return newDatabasesStatus(state, action, DB_STATUS_READY);
case ORBIT_DATABASE_REPLICATING:
return newDatabasesStatus(state, action, DB_STATUS_REPLICATING);
case ORBIT_DATABASE_REPLICATED:
return newDatabasesStatus(state, action, DB_STATUS_REPLICATED);
default:
return state;
}
};
function newDatabasesStatus (state, action, status) {
return {
...state,
databases:{
...state.databases,
[action.database.id]: {
...state[action.database.id],
status
}
}
}
}
export default orbitReducer;

96
src/orbit/orbitSaga.js

@ -0,0 +1,96 @@
import {all, call, put, take, takeLatest} from 'redux-saga/effects'
import OrbitDB from 'orbit-db';
import Identities from 'orbit-db-identity-provider'
import {
ORBIT_DATABASE_CREATED,
ORBIT_DATABASE_CREATING,
ORBIT_DATABASE_FAILED,
ORBIT_DATABASE_LISTEN,
ORBIT_IDENTITY_PROVIDER_ADD,
ORBIT_IDENTITY_PROVIDER_ADDED,
ORBIT_IDENTITY_PROVIDER_FAILED,
ORBIT_INIT_FAILED,
ORBIT_INITIALIZED,
ORBIT_INITIALIZING
} from './orbitActions';
import { resolveOrbitDBTypeFun} from "./orbitUtils";
const LOGGING_PREFIX = 'orbitSaga: ';
/*
* Add Orbit Identity Provider
*/
export function * addOrbitIdentityProvider(identityProvider) {
try {
yield put({ type: ORBIT_IDENTITY_PROVIDER_ADD });
// Add Identity Provider
Identities.addIdentityProvider(identityProvider);
yield put({ type: ORBIT_IDENTITY_PROVIDER_ADDED });
} catch (error) {
yield put({ type: ORBIT_IDENTITY_PROVIDER_FAILED, error });
console.error(LOGGING_PREFIX + 'EthereumIdentityProvider adding error:');
console.error(error);
}
}
export function * initOrbit(action) {
try {
let { breeze, id } = action;
const { ipfs } = breeze;
const { identityProvider, databases } = breeze.orbitOptions;
const identity = yield call(Identities.createIdentity, { id, type: identityProvider.type});
const orbit = yield call (OrbitDB.createInstance, ...[ipfs, { identity }]);
breeze.orbit = orbit;
// Create our own initial databases, as given in the options
yield all(databases.map(db => {
return call(createDatabase, { orbit, db });
}));
yield put({ type: ORBIT_INITIALIZED, orbit });
return orbit;
} catch (error) {
yield put({ type: ORBIT_INIT_FAILED, error });
console.error(LOGGING_PREFIX + 'OrbitDB initialization error:');
console.error(error);
}
}
/*
* Creates an orbit database given a name and a type as its parameters
*/
export function * createDatabase({orbit, db}) {
try {
const dbTypeFun = resolveOrbitDBTypeFun(orbit, db.type);
const createdDB = yield call([orbit, dbTypeFun], db.name);
yield put({ type: ORBIT_DATABASE_CREATED, database: createdDB });
// Wait for event channel setup before loading
yield take(action => action.type === ORBIT_DATABASE_LISTEN && action.id === createdDB.id);
yield call([createdDB, createdDB.load]);
return createdDB;
} catch (error) {
yield put({ type: ORBIT_DATABASE_FAILED, error });
console.error(LOGGING_PREFIX + 'OrbitDB identity provider adding error:');
console.error(error);
}
}
function * orbitSaga () {
yield takeLatest(ORBIT_INITIALIZING, initOrbit);
yield takeLatest(ORBIT_DATABASE_CREATING, createDatabase);
}
export default orbitSaga

61
src/orbit/orbitStatusSaga.js

@ -0,0 +1,61 @@
import { call, put, take, takeEvery } from 'redux-saga/effects'
import {eventChannel} from "@redux-saga/core";
import {
ORBIT_DATABASE_CREATED,
ORBIT_DATABASE_LISTEN,
ORBIT_DATABASE_READY,
ORBIT_DATABASE_REPLICATED,
ORBIT_DATABASE_REPLICATING
} from './orbitActions';
/*
* Database Events
* See also https://redux-saga.js.org/docs/advanced/Channels.html
*/
function createOrbitDatabaseChannel (database){
return eventChannel(emit => {
const onReady = () => {
emit({ type: ORBIT_DATABASE_READY, database });
};
const onReplicate = () => {
emit({ type: ORBIT_DATABASE_REPLICATING, database });
};
const onReplicated = () => {
emit({ type: ORBIT_DATABASE_REPLICATED, database });
};
const eventListener = database.events
.on('ready', onReady)
.on('replicate', onReplicate)
.on('replicated', onReplicated)
return () => {
eventListener.removeListener('ready',onReady)
eventListener.removeListener('replicate',onReplicate)
eventListener.removeListener('replicated',onReplicated)
};
})
}
export function * callListenForOrbitDatabaseEvent ({database}) {
const orbitDatabaseChannel = yield call(createOrbitDatabaseChannel, database)
yield put({type: ORBIT_DATABASE_LISTEN, id: database.id});
try {
while (true) {
let event = yield take(orbitDatabaseChannel);
yield put(event);
}
} finally {
orbitDatabaseChannel.close();
}
}
function * orbitStatusSaga () {
yield takeEvery(ORBIT_DATABASE_CREATED, callListenForOrbitDatabaseEvent);
}
export default orbitStatusSaga

25
src/orbit/orbitUtils.js

@ -0,0 +1,25 @@
import * as orbitTypes from "./constants";
export function resolveOrbitDBTypeFun(orbitdb, type){
let dbTypeFun;
switch(type) {
case orbitTypes.ORBIT_TYPE_LOG:
dbTypeFun = orbitdb.log;
break;
case orbitTypes.ORBIT_TYPE_FEED:
dbTypeFun = orbitdb.feed;
break;
case orbitTypes.ORBIT_TYPE_KEYVALUE:
dbTypeFun = orbitdb.keyvalue;
break;
case orbitTypes.ORBIT_TYPE_DOCS:
dbTypeFun = orbitdb.docs;
break;
case orbitTypes.ORBIT_TYPE_COUNTER:
dbTypeFun = orbitdb.counter;
break;
default:
throw "Invalid OrbitDB type!";
}
return dbTypeFun;
}

6763
yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save