Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@cerebral/firebase
Advanced tools
NPM
npm install @cerebral/firebase
The Firebase provider is a Cerebral friendly wrapper around the Firebase client. By default the Firebase client is heavily event based, even just getting some value, handling authentication etc. This is useful in some types of apps, but Cerebral has a very straight forward way of thinking about side effects. You will find that a lot of the API exposed by the Firebase client is simplified!
import {Controller} from 'cerebral'
import FirebaseProvider from '@cerebral/firebase'
const controller = Controller({
providers: [
FirebaseProvider({
config: {
apiKey: '{apiKey}',
authDomain: '{authDomain}',
databaseURL: '{databaseURL}',
storageBucket: '{storageBucket}'
},
// When using tasks and firebase queue you can prefix
// the specs triggered. This is useful in development
// when multiple developers are working on the
// same instance
specPrefix: 'CJ'
})
]
})
Important notes
The Cerebral firebase provider uses dot notation to keep consistency with Cerebral itself
All factories supports template tags, allowing you to dynamically create paths and points to values
import {FirebaseProviderError} from '@cerebral/firebase'
// Error structure
{
name: 'HttpProviderError',
message: 'Some firebase error message'
stack: '...'
}
import {FirebaseProviderAuthenticationError} from '@cerebral/firebase'
// Error structure
{
name: 'HttpProviderError',
message: 'Some firebase error message'
code: 10 // firebase auth error code
stack: '...'
}
Write data to this database location. This will overwrite any data at this location and all child locations. Passing null for the new value is equivalent to calling remove(); all data at this location or any child location will be deleted.
action
function someAction({firebase}) {
return firebase.set('foo.bar', 'baz')
}
factory
import {props} from 'cerebral/tags'
import {set} from '@cerebral/firebase/operators'
export default [
set('foo.bar', props`foo`),
// Alternatively with explicit paths
set('foo.bar', props`foo`), {
success: [],
error: []
}
]
output No output
As opposed to the set() method, update() can be use to selectively update only the referenced properties at the current location (instead of replacing all the child properties at the current location).
action
function someAction({firebase}) {
return firebase.update('some.path', {
'foo': 'bar',
'items.item1.isAwesome': true
})
}
factory
import {props} from 'cerebral/tags'
import {update} from '@cerebral/firebase/operators'
export default [
update('some.path', {
'foo.bar': props`bar`,
'foo.baz': props`baz`
}),
// Alternatively with explicit paths
update('some.path', {
'foo.bar': props`bar`,
'foo.baz': props`baz`
}), {
success: [],
error: []
}
]
output No output
Generates a new child location using a unique key and returns its reference from the action. An example being {key: "-KWKImT_t3SLmkJ4s3-w"}
.
action
function someAction({firebase}) {
return firebase.push('users', {
name: 'Bob'
})
}
factory
import {state} from 'cerebral/tags'
import {push} from '@cerebral/firebase/operators'
export default [
push('users', state`newUser`),
// Alternatively with explicit paths
push('users', state`newUser`), {
success: [],
error: []
}
]
output
{key: 'theAddedKey'}
Remove the data at this database location.
action
function someAction({ firebase}) {
return firebase.remove('foo.bar')
}
factory
import {props, string} from 'cerebral/tags'
import {remove} from '@cerebral/firebase/operators'
export default [
remove(string`users.${props`userKey`}`),
// Alternatively with explicit paths
remove(string`users.${props`userKey`}`), {
success: [],
error: []
}
]
output No output
Atomically modifies the data at the provided location.
Unlike a normal set(), which just overwrites the data regardless of its previous value, transaction() is used to modify the existing value to a new value, ensuring there are no conflicts with other clients writing to the same location at the same time.
To accomplish this, you pass transaction() an update function which is used to transform the current value into a new value. If another client writes to the location before your new value is successfully written, your update function will be called again with the new current value, and the write will be retried. This will happen repeatedly until your write succeeds without conflict or you abort the transaction by not returning a value from your update function.
action
function someAction({firebase}) {
function transactionFunction(currentData){
if (currentData === null) {
return { foo: 'bar' }
}
return // Abort the transaction.
}
return firebase.transaction('some.transaction.path', transactionFunction)
.then((result) => {
if(result.committed){
return {result: result.value}
} else {
throw new Error('Transaction failed')
}
})
}
factory
import {transaction} from '@cerebral/firebase/operators'
function transactionFunction(currentData){
if (currentData === null) {
return { foo: 'bar' }
}
return // Abort the transaction.
}
export default [
transaction('foo.bar', transactionFunction),
// Alternatively with explicit paths
transaction('foo.bar', transactionFunction), {
success: [],
error: []
}
]
output
{committed: true, value: 'new value'}
Note: Modifying data with set() will cancel any pending transactions at that location, so extreme care should be taken if mixing set() and transaction() to update the same data.
Note: When using transactions with Security and Firebase Rules in place, be aware that a client needs .read access in addition to .write access in order to perform a transaction. This is because the client-side nature of transactions requires the client to read the data in order to transactionally update it.
action
function someAction({ firebase }) {
return firebase.value('someKey.foo', {
limitToFirst: 5, // Read Firebase docs
limitToLast: 5, // Read Firebase docs
startAt: 5, // Read Firebase docs
endAt: 5, // Read Firebase docs
equalTo: 5, // Read Firebase docs
orderByChild: 'count', // Read Firebase docs
orderByKey: true, // Read Firebase docs
orderByValue: true, // Read Firebase docs
// Set to true to get an array of {key: '', value: {}}
// Useful with ordering options
asArray: false,
})
}
factory
import {value} from '@cerebral/firebase/operators'
import {state} from 'cerebral/tags'
export default [
value('foo.bar', {
asArray: true,
orderByChild: state`currentOrderChild`
}),
// Alternatively with explicit paths
value('foo.bar'), {
success: [],
error: []
}
]
output
// Normally
{key: 'keyYouLookedUpValueOn', value: 'the value'}
// With "asArray"
[{key: 'keyYouLookedUpValueOn', value: 'the value'}, {key: 'keyYouLookedUpValueOn', value: 'the value'}]
action
function someAction({ firebase }) {
firebase.onValue('someKey.foo', 'someModule.fooUpdated', {
payload: {}, // Merged with the payload passed on new data
});
}
This will NOT immediately grab the value and trigger the signal passed, the first event is discarded for more predictable behaviour. To grab existing value, just use value
.
To stop listening for updates to the value:
function someAction({ firebase }) {
firebase.off('someKey.foo', 'onValue');
}
factory
import {onValue} from '@cerebral/firebase/operators'
export default [
onValue('foo.bar', 'some.signal')
]
action
function someAction({ firebase }) {
firebase.onChildAdded('posts', 'posts.postAdded', {
payload: {}, // Merged with the payload passed on new data
limitToFirst: 5, // Read Firebase docs
limitToLast: 5, // Read Firebase docs
startAt: 5, // Read Firebase docs
endAt: 5, // Read Firebase docs
equalTo: 5, // Read Firebase docs
orderByChild: 'count', // Read Firebase docs
orderByKey: true, // Read Firebase docs
orderByValue: true // Read Firebase docs
});
}
This will immediately grab and trigger the signal posts.postAdded
for every post grabbed. Note this is just registering a listener, not returning a value from the action. The signal is triggered with the payload: { key: 'someKey', value: {} }
.
To stop listening for updates to the posts:
function someAction({ firebase }) {
firebase.off('posts', 'onChildAdded');
}
factory
import {state} from 'cerebral/tags'
import {onChildAdded} from '@cerebral/firebase/operators'
export default [
onChildAdded('foo.bar', 'some.signal', {
orderByChild: 'count',
limitToFirst: state`config.limitToFirst`
})
]
action
function someAction({ firebase }) {
firebase.onChildRemoved('posts', 'posts.postRemoved', {
// Same options as above
});
}
This will trigger the signal posts.postRemoved
whenever a post is removed from the selection. The signal is triggered with the payload: { key: 'someKey' }
.
To stop listening:
function someAction({ firebase }) {
firebase.off('posts', 'onChildRemoved');
}
factory
import {onChildRemoved} from '@cerebral/firebase/operators'
export default [
onChildRemoved('foo.bar', 'some.signal', {
// Same options as above
})
]
action
function someAction({ firebase }) {
firebase.onChildChanged('posts', 'posts.postChanged', {
// Same options as above
});
}
This will trigger the signal posts.postChanged
whenever a post is changed in the selection. The signal is triggered with the payload: { key: 'someKey', value: {} }
.
To stop listening:
function someAction({ firebase }) {
firebase.off('posts', 'onChildChanged');
}
factory
import {onChildChanged} from '@cerebral/firebase/operators'
export default [
onChildChanged('foo.bar', 'some.signal', {
// Same options as above
})
]
action
function someAction({ firebase }) {
// Turn off listener at specific path and
// specific event
firebase.off('posts', 'onChildChanged')
// Turn off all listeners at specific path
firebase.off('posts', 'onChildChanged')
// Turn off all listeners of specific event at wildcard path
firebase.off('posts.*', 'onChildChanged')
// Turn off all listeners at wildcard path
firebase.off('posts.*')
}
factory
import {string} from 'cerebral/tags'
import {off} from '@cerebral/firebase/operators'
export default [
// Same API as in actions, also wildcard
off('posts', 'onChildChanged'),
// Compose using string tag
off(string`posts.${state`selectedPostKey`}`)
]
If you are using the firebase-queue and need to create tasks, you can do that with:
action
function someAction({ firebase, state }) {
return firebase.task('create_post', {
uid: state.get('app.user.uid'),
text: state.get('posts.newPostText')
})
}
This will add a task at queue/tasks
. There is no output from a resolved task, it just resolves when the action has been processed.
factory
import {state, props} from 'cerebral/tags'
import {task} from '@cerebral/firebase/operators'
export default [
task('some_task', {
uid: state`user.uid`,
data: props`data`
}),
// Alternatively with explicit paths
task('some_task', {
uid: state`user.uid`,
data: props`data`
}), {
success: [],
error: []
}
]
output No output
Will resolve to {user: {}}
if user exists. If user was redirected from Facebook/Google etc. as part of first sign in, this method will handle the confirmed registration of the user.
action
function someAction({ firebase }) {
return firebase.getUser()
}
factory
import {getUser} from '@cerebral/firebase/operators'
export default [
getUser(),
// Alternatively with explicit paths
getUser(), {
success: [],
error: []
}
]
output
{user: {}}
This login will method will resolve to existing anonymous or create a new one for you.
action
function someAction({ firebase }) {
return firebase.signInAnonymously()
}
factory
import {signInAnonymously} from '@cerebral/firebase/operators'
export default [
signInAnonymously(),
// Alternatively with explicit paths
signInAnonymously(), {
success: [],
error: []
}
]
output
{user: {}}
Register a new user with email and password.
action
function someAction({ firebase, state }) {
const email = state.get('register.email')
const password = state.get('register.password')
return firebase.createUserWithEmailAndPassword(email, password)
}
factory
import {state} from 'cerebral/tags'
import {createUserWithEmailAndPassword} from '@cerebral/firebase/operators'
export default [
createUserWithEmailAndPassword(state`newUser.email`, state`newUser.password`),
// Alternatively with explicit paths
createUserWithEmailAndPassword(state`newUser.email`, state`newUser.password`), {
success: [],
error: []
}
]
output
{user: {}}
Sign in a user with email and password.
action
function someAction({ firebase, state }) {
const email = state.get('register.email')
const password = state.get('register.password')
return firebase.signInWithEmailAndPassword(email, password)
}
factory
import {props} from 'cerebral/tags'
import {signInWithEmailAndPassword} from '@cerebral/firebase/operators'
export default [
signInWithEmailAndPassword(props`email`, props`password`),
// Alternatively with explicit paths
signInWithEmailAndPassword(props`email`, props`password`), {
success: [],
error: []
}
]
output
{user: {}}
Sign in a user with Facebook, Google or Github.
action
function someAction({ firebase, state }) {
return firebase.signInWithFacebook({
redirect: false, // Use popup or redirect. Redirect typically for mobile
scopes: [] // Facebook scopes to access
})
}
factory
import {state} from 'cerebral/tags'
import {signInWithFacebook} from '@cerebral/firebase/operators'
export default [
signInWithFacebook({
redirect: state`useragent.media.small`
}),
// Alternatively with explicit paths
signInWithFacebook({
redirect: state`useragent.media.small`
}), {
success: [],
error: []
}
]
output Either this will be a redirect or it outputs:
{user: {}}
Similar you can sign in with Google or GitHub.
Just use signInWithGoogle
or signInWithGithub
instead of signInWithFacebook
.
Sign in a custom token.
action
function someAction({ firebase, state }) {
return firebase.signInWithCustomToken(state.get('token'))
}
factory
import {props, state} from 'cerebral/tags'
import {signInWithCustomToken} from '@cerebral/firebase/operators'
export default [
signInWithCustomToken(props`token`),
// Alternatively with explicit paths
signInWithCustomToken(state`token`), {
success: [],
error: []
}
]
output
{user: {}}
Link an anonymous account with Facebook, Google or Github.
action
function someAction({ firebase, state }) {
return firebase.linkWithFacebook({
redirect: false, // Use popup or redirect. Redirect typically for mobile
scopes: [] // Facebook scopes to access
})
}
factory
import {state} from 'cerebral/tags'
import {linkWithFacebook} from '@cerebral/firebase/operators'
export default [
linkWithFacebook({
redirect: state`useragent.media.small`
}),
// Alternatively with explicit paths
linkWithFacebook({
redirect: state`useragent.media.small`
}), {
success: [],
error: []
}
]
output Either this will be a redirect or it outputs:
{user: {}}
Similar you can sign in with Google or GitHub.
Just use linkWithGoogle
or linkWithGithub
instead of linkWithFacebook
.
Sign out user. getUser will now not resolve a user anymore.
action
function someAction({ firebase }) {
return firebase.signOut()
}
factory
import {signOut} from '@cerebral/firebase/operators'
export default [
signOut(),
// Alternatively with explicit paths
signOut(), {
success: [],
error: []
}
]
output No output
action
function someAction({ firebase, state }) {
return firebase.sendPasswordResetEmail(state.get('user.email'))
}
factory
import {state} from 'cerebral/tags'
import {sendPasswordResetEmail} from '@cerebral/firebase/operators'
export default [
sendPasswordResetEmail(state`user.email`),
// Alternatively with explicit paths
sendPasswordResetEmail(state`user.email`), {
success: [],
error: []
}
]
output No output
Sets a value when Firebase detects user has disconnected.
action
function someAction({ firebase, state }) {
firebase.setOnDisconnect(`activeUsers.${state.get('app.user.uid')}`, 'someValue')
}
factory
import {state} from 'cerebral/tags'
import {setOnDisconnect} from '@cerebral/firebase/operators'
export default [
setOnDisconnect(string`activeUsers.${state`app.user.uid`}`, null)
]
output No output
Cancel setting a value when Firebase detects disconnect.
action
function someAction({ firebase, state }) {
firebase.cancelOnDisconnect()
}
factory
import {state} from 'cerebral/tags'
import {cancelOnDisconnect} from '@cerebral/firebase/operators'
export default [
cancelOnDisconnect()
]
output No output
Upload a new file at the given location. Please note that the file is not stored inside the realtime database but into Google Cloud Storage (please consult filrebase documentation). This means that you need to take care of storage security as well.
Note that put
expects a folder as first argument and will use the name of the provided file. If you want to control the filename, add this in the options. In this case, make sure to respect file type and extension...
action
function someAction({ firebase, props }) {
return firebase.put('folderName', props.file, {
progress({progress, bytesTransferred, totalBytes, state}) {
/* do whatever */
},
// Override name, make sure you set same extension
filename: 'customName.png'
})
}
factory
import {props, signal, string, state} from 'cerebral/tags'
import {put} from '@cerebral/firebase/operators'
// we expect props.file to contain a file provided by
// a user in an <input type='file' />
export default [
put(string`posts.all.${props`postId`}`, props`file`, {
// Trigger a signal which receives payload
progress: signal`gallery.progress`
// Set progress on a state value
progress: state`gallery.progress`
}),
// Alternatively with explicit paths
put(string`posts.all.${props`postId`}`, props`file`, {
progress: signal`gallery.progress`
progress: state`gallery.progress`
}), {
success: [],
error: []
}
]
output
{url: 'urlToFile', filename: 'nameOfFile'}
Use delete
to remove an uploaded file. Specify the containing folder and filename.
action
function someAction({ firebase, props }) {
return firebase.delete('folderName', props.fileName)
}
factory
import {props, state, string} from 'cerebral/tags'
import {delete as firebaseDelete} from '@cerebral/firebase/operators'
export default [
firebaseDelete(
string`posts.all.${props`postId`}`,
state`posts.all.${props`postId`}.imageName`
),
// Alternatively with explicit paths
firebaseDelete(
string`posts.all.${props`postId`}`,
state`posts.all.${props`postId`}.imageName`
), {
success: [],
error: []
}
]
output No output
FAQs
Firebase provider for Cerebral
The npm package @cerebral/firebase receives a total of 57 weekly downloads. As such, @cerebral/firebase popularity was classified as not popular.
We found that @cerebral/firebase demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.