Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
c8osdkangular
Advanced tools
Click here to see API documentation
This is the Convertigo provider for Angular
Convertigo Client SDK is a set of libraries used by mobile or Windows desktop applications to access Convertigo Server services. An application using the SDK can easily access Convertigo services such as Sequences and Transactions.
The Client SDK will abstract the programmer from handling the communication protocols, local cache, FullSync off line data management, UI thread management and remote logging. So the developer can focus on building the application.
Client SDK is available for:
This current package is the Angular SDK. For others SDKs see official Convertigo Documentation..
Convertigo Mobility Platform supports Angular developers. Services brought by the platform are available for Angular clients applications thanks to the Convertigo MBaaS SDK. SDK provides an Angular framework you can use to access Convertigo Servers services such as:
Convertigo Technology Overview
Access Convertigo mBaaS technical documentation
Access Convertigo SDK Documentations
$ npm install --save c8osdkangular@latest
To target angular 13+ (with ivy support) please use at least version 4.0.16
$ npm install --save c8osdkangular@angular13
NB: Both branches of sdk ( 3.X and 4.X ) supports the same features, only the builded package changes
To target angular 5+ (without ivy support) please use at least version 3.0.0
$ npm install --save c8osdkangular@angular5
NB: Both branches of sdk ( 3.X and 4.X ) supports the same features, only the builded package changes
Then add the following lines into your polyfill.ts located at /ProjectRoot/src/polyfill.ts
(window as any).global = window;
(window as any).process = window;
window["browser"] = true;
(window as any).global.Buffer = (window as any).global.Buffer || require('buffer').Buffer;
if((window as any).process != undefined){
(window as any).process.nextTick = function() {
return null;
};
}
else{
(window as any).process = {
env: { DEBUG: undefined },
nextTick: function() {
return null;
}
};
}
In Angular dependency injection is used to create a C8o instance
//Into app.module.ts
import {C8o} from "c8osdkangular";
import {HttpClientModule} from "@angular/common/http";
@NgModule({
imports: [
BrowserModule,
HttpClientModule
],
providers: [
C8o
]
});
//Into the target page
import { C8o, C8oSettings } from "c8osdkangular";
export class MyClass {
// Instanciate c8o thanks to Angular di (dependency injection)
constructor(private c8o: C8o) {
// Call init method with a c8osettings class giving an endpoint
this.c8o.init(new C8oSettings().setEndPoint("http://localhost:18080/convertigo/projects/template_Ionic2"));
// Use the specific method to be sure that init has been finished
this.c8o.finalizeInit().then(()=>{
// Do stuff with c8o object
});
}
}
This feature is supported from c8osdkangular@3.0.8 and convertigo server 7.8
See CSRF for more information.
//Into app.module.ts
import {C8o, HttpXsrfInterceptor} from "c8osdkangular";
import {HttpClientModule, HTTP_INTERCEPTORS} from "@angular/common/http";
@NgModule({
imports: [
BrowserModule,
HttpClientModule
],
providers: [
C8o,
{
provide: HTTP_INTERCEPTORS,
useClass: HttpXsrfInterceptor,
multi: true
},
]
});
The endpoint is the mandatory setting to get a C8o instance correctly initialized but there is additional settings through the C8oSettings class.
A C8oSettings instance should be passed after the endpoint. Settings are copied inside the C8o instance and a C8oSettings instance can be modified and reused.
Setters of C8oSettings always return its own instance and can be chained.
A C8oSettings can be instantiated from an existing C8oSettings or C8o instance.
In order to finish the c8o initialization you must use init method from c8o object with a c8oSettings parameters
import { C8o, C8oSettings } from "c8osdkangular";
// The only way
let settings: C8oSettings = new C8oSettings();
settings
.setEndPoint("https://demo.convertigo.net/cems/projects/sampleMobileCtfGallery")
.setDefaultDatabaseName("mydb_fullsync")
.setTimeout(30000)
.setKeepSessionAlive(true);
//Then we need to assign C8oSettings object to our C8o object
c8o.init(settings);
// all settings can be retrieve from a C8o or C8oSettings instance
let timeout : number = c8o.timeout;
If you want to normalize parameters of every call you can set it throw C8osettings setNormalizeParameters method. setNormalizeParameters
import { C8o, C8oSettings } from "c8osdkangular";
// The only way
let settings: C8oSettings = new C8oSettings();
settings
.setNormalizeParameters(true);
//Then we need to assign C8oSettings object to our C8o object
c8o.init(settings);
// all settings can be retrieve from a C8o or C8oSettings instance
let normalizeParameters : boolean = c8o.normalizeParameters;
If you wants convertigo's error to be throw, into fail handler, you can set the following setting:
// The only way
let settings: C8oSettings = new C8oSettings();
settings
.setErrorConvertigoIntoFail(true);
//Then we need to assign C8oSettings object to our C8o object
c8o.init(settings);
With a C8o instance you can call Convertigo Sequence and Transaction or make query to your local FullSync database.
The call method expects the requester string of the following syntax:
For a transaction: [project].connector.transaction For a sequence: [project].sequence The project name is optional, i.e. if not specified, the project specified in the endpoint will be used.
import { C8o, C8oSettings } from "c8osdkangular"
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above, and '.login' is the name of a sequence of your project
// Here using Javascript's Promises with awaiter syntax
let result = await this.c8o.callJson('.login')
.async();
// Here using Javascript's Promises with then/catch syntax
this.c8o.callJson(".login")
.async()
.then((response)=>{
//handle result
});
// Using RxJS Observables that allow for example progress and Live.
this.c8o.callJson(".login")
.toObservable()
.subscribe(next => {
// handle results
// console.log(next.response);
//console.log(next.parameters)
},
error =>{
// handle errors
},
() =>{
// handle complete
});
// Using C8oPromise that allow for example progress and Live. C8oPromise is described in Api doc in section Api documentation of this README
this.c8o.callJson(".login")
.then((response)=>{
//handle result
});
Convertigo requestables generally needs key/value parameters encapsulated in a simple javascript object.
The key is always a string and the value can be any object but a string is the standard case.
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above, and '.login' is the name of a sequence of your project
// Here using Javascript's Promises with awaiter syntax
let result = await this.c8o.callJsonObject('.login', {
login: "barnett.christine",
password: "mySuperPassword123"
})
.async();
// Here using Javascript's Promises with then/catch syntax
this.c8o.callJsonObject('.login', {
login: "barnett.christine",
password: "mySuperPassword123"
})
.async()
.then((response)=>{
// handle result
});
// Using C8oPromise that allow for example progress and Live. C8oPromise is described in Api doc in section Api documentation of this README.
this.c8o.callJsonObject(".login",{
login: "barnett.christine",
password: "mySuperPassword123"
})
.then((response)=>{
//handle result
});
Convertigo requestables supports file as argument. You can pass it as key value pair. Indeed, the key is a string (name of the variable), and the value must be a Javascript File, or FileList. It can be into an array or directly passed as argument
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above, and '.login' is the name of a sequence of your project
// Here using Javascript's Promises with awaiter syntax
let fileFirst = new File(["Hello Convertigo First !"], "fileFirst.txt", {
type: "text/plain",
});
let fileSecond = new File(["Hello Convertigo Second !"], "fileSecond.txt", {
type: "text/plain",
});
let result = await this.c8o.callJson('.sequenceThatReceiveAFile ', {
paramOne: "MyFirstParam",
files: [fileFirst, fileSecond]
})
.async();
// Here using Javascript's Promises with then/catch syntax
this.c8o.callJson('.sequenceThatReceiveAFile', {
paramOne: "MyFirstParam",
files: [fileFirst, fileSecond]
})
.async()
.then((response)=>{
// handle result
});
// Using C8oPromise that allow for example progress and Live. C8oPromise is described in Api doc in section Api documentation of this README.
this.c8o.callJson(".sequenceThatReceiveAFile",{
paramOne: "MyFirstParam",
files: [fileFirst, fileSecond]
})
.then((response)=>{
//handle result
});
The .then() returns a C8oPromise that can be use to chain other promise methods, such as .then() or failure handlers. The last .then() must return a null value. .then() can be mixed but the returning type must be the same: XML or JSON.
c8o.callJson(".getSimpleData", "callNumber", 1)
.then((response) => {
// you can do stuff here and return the next C8oPromise instead of deep nested blocks
return c8o.callJson(".getSimpleData", "callNumber", 2);
})
.then((response)=>{
// you can do stuff here and even modify previous parameters
parameters["callNumber"] = 3;
parameters["extraParameter"] = "ok";
return c8o.callJsonObject(".getSimpleData", parameters);
})
.then((response)=>{
// you can do stuff here and return null because this is the end of the chain
return null;
})
A call can throw an error for many reasons: technical failure, network error and so on.
The standard try/catch should be used to handle this.
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above, and '.login' is the name of a sequence of your project
// Here using Javascript's Promises with awaiter
try{
let result = await this.c8o.callJsonObject('.login', {
login: "barnett.christine",
password: "mySuperPassword123"
}).async();
}
catch(error){
// Do something with the error
}
// Here using Javascript's Promises
this.c8o.callJsonObject('.login', {
login: "barnett.christine",
password: "mySuperPassword123"
}).
.async()
.then((response)=>{
//handle result
})
.catch((error)=>{
// Do something with the error
})
// Here using Rxjs Observables
this.c8o.callJson(".login")
.toObservable()
.subscribe(next => {
// handle results
// console.log(next.response);
//console.log(next.parameters)
},
error =>{
// handle errors
},
() =>{
// handle complete
});
// Using C8oPromise that allow for example progress and Live. C8oPromise is described in Api doc in section Api documentation of this README.
this.c8o.callJsonObject('.login', {
login: "barnett.christine",
password: "mySuperPassword123"
}).
.then((response)=>{
//handle result
})
.fail((error)=>{
// Do something with the error
})
An application developer usually adds log information in his code. This is useful for the code execution tracking, statistics or debugging.
The Convertigo Client SDK offers an API to easily log on the standard device logger, generally in a dedicated console. To see this console, a device must be physically connected on a computer.
Fortunately, the same API also send log to the Convertigo server and they are merged with the server log. You can easily debug your device and server code on the same screen, on the same timeline. Logs from a device contain metadata, such as the device UUID and can help to filter logs on the server.
A log level must be specified:
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above
const error = anyError;
c8o.log.fatal("hello logs ! (level fatal)", error);
c8o.log.error("hello logs ! (level error)", error);
c8o.log.warn("hello logs ! (level warn)", error);
c8o.log.info("hello logs ! (level info)", error);
c8o.log.debug("hello logs ! (level debug)", error);
c8o.log.trace("hello logs ! (level trace)", error);
A C8oLogger have 2 log levels, one for local logging and the other for the remote logging. With the Android SDK, the local logging is set by the logcat options. With the .Net SDK, the local logging depends of the LogLevelLocal setting of C8oSettings.
The remote logging level is enslaved by Convertigo server Log levels property: devices output logger. In case of failure, the remote logging is disabled and cannot be re-enabled for the current C8o instance. It can also be disabled using the LogRemote setting of C8oSettings, enabled with true (default) and disabled with false.
C8oSettings()
.setLogC8o(false) // disable log from the Convertigo Client SDK itself
.setLogRemote(false) // disable remote logging
.setLogLevelLocal(C8oLogLevel.TRACE);
Sometimes we would like to use local cache on C8o calls and responses, in order to:
The Local Cache feature allows to store locally on the device the responses to a C8o call, using the variables and their values as cache key.
To use the Local Cache, add to a call a pair parameter of "__localCache" and a C8oLocalCache instance. The constructor of C8oLocalCache needs some parameters:
// Assuming c8o is a C8o instance properly instanciated and initiated as describe above.
// Return the response if is already know and less than 180 seconds else call the server
this.c8o.callJsonObject(".getSimpleData",
{
C8oLocalCache.PARAM: new C8oLocalCache(Priority.LOCAL, 180 * 1000)
})
.then((response)=>{
// Do stuff
});
// same sample but with parameters, also acting as cache keys
this.c8o.callJsonObject(".getSimpleData",
{
"firstname": "John",
"lastname": "Doe",
C8oLocalCache.PARAM: new C8oLocalCache(Priority.LOCAL, 180 * 1000)
})
.then((response)=>{
// Do stuff
});
// make a standard network call with the server
// but in case of offline move or network failure
// return the response if is already know and less than 1 hour
this.c8o.callJsonObject(".getSimpleData",
{
C8oLocalCache.PARAM: new C8oLocalCache(Priority.SERVER, 3600 * 1000)
})
.then((response)=>{
// Do stuff
});
FullSync enables mobile apps to handle fully disconnected scenarios, still having data handled and controlled by back end business logic. See the presentation of the Full Sync architecture for more details.
Convertigo Client SDK provides a high level access to local data following the standard Convertigo Sequence paradigm. They differ from standard sequences by a fs:// prefix. Calling these local Full Sync requestable will enable the app to read, write, query and delete data from the local database:
Where fs:// is the name of a specific FullSync Connector in the project specified in the endpoint. The fs:// name is optional only if the default database name is specified with the method setDefaultDatabaseName on the C8oSetting.
An application can have many databases. On mobile (Android, iOS and Xamarin based) they are stored in the secure storage of the application. On Windows desktop application, they are stored in the user AppData/Local folder, without application isolation.
All platforms can specify a local database prefix that allows many local database copies of the same remote database. Use the method setFullSyncLocalSuffix on the C8oSettings.
If you wants to create programmatically a new database, you must use fs://baseName.create
verb.
let resultCreate = await this.c8o.callJson("fs://mabase.create").async();
If you wants to get informations about a database programmatically , you must use fs://baseName.info
verb.
let resultInfo = await this.c8o.callJson("fs://mabase.info").async();
If you wants to destroy programmatically a database, you must use fs://baseName.destroy
verb.
let resultDestroy = await this.c8o.callJson("fs://mabase.destroy").async();
If you wants to delete all data from a database and the re-create it, you must use fs://baseName.reset
verb.
let resultReset = await this.c8o.callJson('fs://base.reset').async();
If you wants to post a document into a given database, you must use fs://baseName.post
verb.
You can use the following options:
// here we post a document, with a given id
let resultPost = await this.c8o.callJsonObject("fs://mabase.post",
{
'_id':'myID',
property1: 'myfirstproperty',
property2: ['a','b','c'],
property3: {
'e':'E',
'f':'F',
'g':{'a':['h','i','j', 'k'], 'b':'cdef', 'z':'hij'}
}
}
).async();
// Then we wants to override a specific key of our document but merge the rest of the document
let resultPost = await this.c8o.callJsonObject("fs://mabase.post",
{
'_id':'myID',
property4: 'myNewProp',
property3: {
'g':{'a':['k','l','m']}
},
"_use_policy":"merge",
"_use_merge_property3.g.a":"override",
"_use_merge_property3.g.z":"delete"
}
).async();
//The following post will result into the following document
{
'_id':'myID',
property1: 'myfirstproperty',
property2: ['a','b','c'],
property3: {
'e':'E',
'f':'F',
'g':{'a':['k','l','m'], 'b':'cdef'}
},
property4: 'myNewProp'
}
// Alternatively we can use a specific subkey separator using subkeySeparators
let resultPost = await this.c8o.callJsonObject("fs://mabase.post",
{
'_id':'myID',
[C8o.FS_SUBKEY_SEPARATOR]: "~"
property4: 'myNewProp',
property3: {
'g':{'a':['k','l','m']}
},
"_use_policy":"merge",
"_use_merge_property3~g~a":"override",
"_use_merge_property3~g~z":"delete"
}
).async();
//The following post will result into the following document
{
'_id':'myID',
property1: 'myfirstproperty',
property2: ['a','b','c'],
property3: {
'e':'E',
'f':'F',
'g':{'a':['k','l','m'], 'b':'cdef'}
},
property4: 'myNewProp'
}
If you wants to fetch a document from a given database, you must use fs://baseName.get
verb.
You can use the following options:
// here we fetch a document with its attachments
let resultGet = await this.c8o.callJson("fs://mabase.get", "attachments", true).async();
If you wants to delete a document from a given database, you must use fs://baseName.delete
verb.
You can use the following options:
// here we fetch a document with its attachments
let resultGet = await this.c8o.callJson("fs://mabase.delete", "docid", "myId", "_rev", "myRevision").async();
If you wants to put an attachments into a document, you must use fs://baseName.put_attachment
verb.
FullSync allows us to attaches a binary object to a document. This method will update an existing document to add the attachment, so it requires a rev if the document already exists. If the document doesn’t already exist, then this method will create an empty document containing the attachment. What’s the point of attachments? If you’re dealing with large binary data (such as PNGs), you may incur a performance or storage penalty if you naïvely include them as base64- or hex-encoded strings inside your documents.
You can use the following options:
You can:
// Put an attachment into a given document
let resultPut = await this.c8o.callJson("fs://base.put_attachment",
"docid", id, "name", "text2.txt", "content_type", "text/plain", "content", new Blob(["Hello Convertigo !"], {type: "text/plain"})).async();
If you wants to get an attachment from a document, you must use fs://baseName.get_attachment
verb.
You can use the following options:
Alternatively you can also use get_attachment method from c8o class. You need an id of document and the name of the given attachment.
See the following example.
// Put an attachment into a given document
let resultGet = await this.c8o.callJson("fs://baseName.get_attachment",
"docid", id, "name", "text2.txt").async();
// Or alternative api
c8o.get_attachment("docid", "attachment_name")
.then((response)=>{
// get a buffer
// URL can be 'sanitized' by :
// ... sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(response))
})
.catch(()=>{
// catch error
})
If you wants to get an attachment from a document, you must use fs://baseName.delete_attachment
verb.
You can use the following options:
// Put an attachment into a given document
let resultdelete = await this.c8o.callJson("fs://baseName.delete_attachment",
"docid", id, "name", "text2.txt", "rev": "myRev").async();
If you wants to synchronize a database, you must use fs://baseName.sync
verb.
If you wants to replicate pull a database, you must use fs://baseName.replicate_pull
verb.
If you wants to replicate push a database, you must use fs://baseName.replicate_push
verb.
FullSync has the ability to replicate mobile and Convertigo server databases over unreliable connections still preserving integrity. Data can be replicated in upload or download or both directions. The replication can also be continuous: a new document is instantaneously replicated to the other side.
The client SDK offers the progress event to monitor the replication progression thanks to a C8oProgress instance.
A device cannot pull private documents or push any document without authentication. A session must be established before and the Convertigo server must authenticate the session (using the Set authenticated user step for example).
There is 3 ways for the replication:
Theses replications can be done in mode continuous, so if a new document is created its instantaneously replicated to the other side.
Also you can monitor the progress of the replications, and be notified of any error or result...
You can use the following options:
// here we sync (bi-directional) fullsync bases
this.c8o.callJson("fs://mabase.sync", "continuous", true)
.then((response, paremeters)=>{
// in a continous sync, then will be triggered after initial replication
})
.progress((progress: C8oProgress)=>{
// handle progress
})
.fail((error: C8oException)=>{
// handle errors
})
// here we pull (uni-directional) fullsync bases from server to client
this.c8o.callJson("fs://mabase.replicate_pull", "continuous", true)
.then((response, paremeters)=>{
// in a continous replicate_pull, then will be triggered after initial replication
})
.progress((progress: C8oProgress)=>{
// handle progress
})
.fail((error: C8oException)=>{
// handle errors
})
// here we push (uni-directional) fullsync bases from client to server
this.c8o.callJson("fs://mabase.sync", "continuous", true)
.then((response, paremeters)=>{
// in a continous replicate_push, then will be triggered after initial replication
})
.progress((progress: C8oProgress)=>{
// handle progress
})
.fail((error: C8oException)=>{
// handle errors
})
If you wants to bulk load a database, you must use fs://baseName.bulk
verb.
FullSync allow you to bulk load a database from a file.
You can use tools as pouchdb-dump-cli to create a dump file from a couchdb/pouchdb database.
You can use the following options:
// Bulk load of database with an url as data argument
let resultBulk = await this.c8o.callJson("fs://base.bulk", "data", "http://myurl.com/dump.json", "proxy": "https://myserver:5984/mybase").async();
If you wants to perform a view query, you must use fs://baseName.view
verb.
Thanks to the FullSync, you can invoke a map/reduce function, which allows you to perform more complex queries on client side.
Also, it's has the ability to re-execute your query view if the database is modified, it can be local or remote modification, so your data displayed is never out of date, in fact, the callback (then) is called after each modification.
This allow you keep your UI synchronized with database documents.
You can use the following options:
// The then of the live requests can be handled only with C8oPromise
this.c8o.callJsonObject("fs://base.view",{
"ddoc": "design",
"view": "customers",
"__live": "customers"
})
.then((response)=>{
// will be call now and after each database update
})
// cancel the previous FS_LIVE request, can be on application page change for example
this.c8o.cancelLive("customers")
.then(()=>{
// do stuff
}
.catch((err)=>{
// catch errors
});
If you wants to get all documents present in your client side database, you must use .all, or .all_local verbs.
Typically, there is two kind of documents, first kind that are synchronized, and second one that will never be.
If you wants to create local document that will never be synchronized just use prefix _local/ for the id.
To access to (client side) synchronized documents use .all verb, on the other hand if you wants to access to all local documents that will never be synchronised use .all_local.
If you wants to get all document from a given database, you must use fs://baseName.all
verb.
Fetch multiple documents, indexed and sorted by the _id. Deleted documents are only included if options.keys is specified.
This will return document from the store that is not local.
"fs://.all" verb support the following options:
// get all documents including docs
let resultGet = await this.c8o.callJson("fs://base.all", include_docs: true).async();
If you wants to get all locals document from a given database, you must use fs://baseName.all_local
verb.
Thanks to the FullSync, you can fetch multiple documents, indexed and sorted by the _id. Deleted documents are only included if options.keys is specified.
This will return document from the local store.
"fs://.all_local" verb support the following options:
// get all local ("_local/" documents) descending
let resultGet = await this.c8o.callJson("fs://base.all_local", "descending", true).async();
Full Sync has also the ability to notify your if there is any change on the database. The progress following a FS_LIVE parameter is triggered after each database update. The changes contains the origin of the change, and other attributes :
let changeListener : C8oFullSyncChangeListener = new C8oFullSyncChangeListener((changes:Object)=>{
checkChanges(changes);
});
…
c8o.addFullSyncChangeListener("databaseName", changeListener); // add this listener for the database "base" ; null or "" while use the default database.
…
c8o.removeFullSyncChangeListener("databaseName", changeListener); // remove this listener for the database "base" ; null or "" while use the default database.
Sdk has the ability to detect when your session will be dropped, and notify you. This is really helpful, because it can prevent you, in the case of fullsync authenticated replications, from lost of session and so a broken replication.
By default, SDK keep alive your session.
In case of lost of network, we will stop fullsync replications.
And when network come back, we will check if your session is still existing.
In this case we will restart automatically replication.
And if session do not exist anymore we will, try to authenticate you automatically if its not successful we will notify you by a handler.
// set keep sessionAlive active
c8oSettings
.setKeepSessionAlive(true);
// Handle sesion lost event
c8o.handleSessionLost()
.subscribe(() => {
// Do something
});
Sometimes, we do not want SDK to automatically process login, for example in cas of sequence that logout ourselves. So we can pass an argument to the callJSON that will disable autologin for this sequence.
// Disable autologin in order to be able to logout
this.c8o.callJsonObject("project.logout",{
"__disableAutologin": true
})
.then((response)=>{
// logged out
})
In this case we can
This project is a angular basic sample.
It is organized by a workspace that holds the whole project.
Into this workspace there can be several projects.
Actually c8osdkangular is the only one project.
For technical reasons, c8osdkjscore is linked to this repo as a submodule into projects/src/c8osdk-js-core.
c8osdkangular source content is under projects/src/lib.
To build project please run the following command from root of workspace
npm run build-sdk
This command will build the project into dist/ and copy README.md from projects/c8osdkangular/README.md to root of project and dist folder.
To test project please run the following command from root of workspace
npm run test-sdk
Build status on README.md is linked to circle-ci build version please update version number before committing.
The README.md that has to be edited is under projects/README.md if you want the changes to be taken into account
To publish the package please run the following command
npm run publish-sdk
To publish beta the package please run the following command
npm run publish-beta-sdk
FAQs
convertigo's MBaaS client API service
We found that c8osdkangular demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 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
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.