Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Build StatusCoverage Status

Example todo app using AngularJS.Same app using Meteor's front-end.

#asteroid

A javascript client (browser and node) for a Meteor backend.

##Table of contents

Why

Install

Example usage

Advantages over the canonical Meteor front-end

Build asteroid locally

Contribute

API

##Why

Meteor is an awesome platform, but its canonical front-end is not very flexible. Asteroid gives the possibility to connect to a Meteor backend with any JS app.

Some of the things Asteroid allows you to do are:

  • make any existing application reactive

  • use any front-end framework you want with Meteor

  • develop browser extensions backed by Meteor

Blog post on the library

##Install

###In the browser

First, dowload the library:

bower install asteroid

Then, add the necessary libraries to your index.html:

<script src="bower_components/ddp.js/src/ddp.js"></script>
<script src="bower_components/q/q.js"></script>
<script src="bower_components/asteroid/dist/asteroid.browser.js"></script>

If you want to login via oauth providers (facebook, google etc), also include the appropriate plugin:

<script src="bower_components/asteroid/dist/plugins/facebook-login.js"></script>

For facebook connect support in cordova via the facebook connect plugin, see https://github.com/keyvanfatehi/asteroid-facebook-connect

###In a chrome extension or in cordova

Just replace asteroid.browser.js with asteroid.chrome.js or asteroid.cordova.js.

If using from within a chrome extension make sure to request for the tabs and storage permissions in your extensions manifest file.

###In node

Download the package:

npm install asteroid

Require it in your project:

var Asteroid = require("asteroid");

##Example usage

// Connect to a Meteor backendvarceres=newAsteroid("localhost:3000");// Use real-time collectionsceres.subscribe("tasksPublication");vartasks=ceres.getCollection("tasks");tasks.insert({description: "Do the laundry"});// Get the taskvarlaundryTaskRQ=tasks.reactiveQuery({description: "Do the laundry"});// Log the array of resultsconsole.log(laundryTaskRQ.result);// Listen for changeslaundryTaskRQ.on("change",function(){console.log(laundryTaskRQ.result);});// Login your userceres.loginWithTwitter();// Call method and use promises via the Q libraryvarret=ceres.call('newUser');ret.result.then(function(result){console.log('Success:',result);}).catch(function(error){console.error('Error:',error);});

Please refer to the Q documentation for more information about handling promises.

##Advantages over the canonical Meteor front-end

  • Small footprint.

  • Framework agnostic. Use the tools you already know and love to build your app.

  • Allows to use Meteor as a full-blown backend or just as a real-time platform pluggable into any existing project.

  • Easily connect to multiple Meteor servers at the same time, perfect for building admin interfaces.

##Build asteroid locally

Clone the repository (or your fork) on your computer.

git clone https://github.com/mondora/asteroid

Enter the project's directory and install the required dependencies:

cd asteroid/
npm install

Start the development environment (requires gulp installed globally):

gulp

Visit localhost:8080/browser.html and localhost:8080/node.html for unit tests result.

##Contribute

Contributions are as always very very welcome. If you want to help but don't know how to get started, feel free to schedule a pair programming session with me!

##API

##Asteroid methods

###new Asteroid(host, ssl, interceptor)

Creates a new Asteroid instance, that is, a connection to a Meteor server (via DDP).

After being constructed, the instance will connect itself to the Meteor backend. It will also try, upon connection, to resume a previous login session (with a token saved in localstorage). The Asteroid.resumeLoginPromise property stores a promise which will be resolved if the resume was successful, rejected otherwise.

If SockJS is defined, it will be used as the socket transport. Otherwise WebSocket will be used. Note that SockJS is required for IE9 support.

#####Arguments

  • hoststringrequired: the address of the Meteor server, e.g. example.meteor.com

  • sslbooleanoptional: whether to use SSL. Defaults to false.

  • interceptorfunctionoptional: a function which will intercept any socket event. It will be called with an event object containing the name of the event, the timestamp of the event, and details about the event (for instance, in case of a "socket_message_received" event, it'll contain the payload of the message).

#####Returns

An Asteroid instance.


###Asteroid.on(event, handler)

Registers an event handler for the specified event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler.

An Asteroid instance emits the following events:

  • connected: emitted when the DDP connection is established. No arguments are passed to the handler.

  • login: emitted when the user logs in. The id of the logged in user will be passed as argument to the handler.

  • logout: emitted when the user logs out. No arguments are passed to the handler.

#####Returns

Nothing


###Asteroid.loginWith ... ()

Logs the user in via the specified third party (oauth) service.

#####Available services

  • facebook: loginWithFacebook

  • google: loginWithGoogle

  • twitter: loginWithTwitter

  • github: loginWithGithub

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with the error.


###Asteroid.createUser(usernameOrEmail, password, profile)

Creates a user and logs him in. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

  • profileobjectoptional: a blackbox, you can throw anything in here and it'll end up into user.profile.

#####Returns

A promise which will be resolved with the logged user id if the creation and login are successful. Otherwise it'll be rejected with an error.


###Asteroid.loginWithPassword(usernameOrEmail, password)

Logs the user in username/email and password. Does not hash the password before sending it to the server. This is not a problem, since you'll probably be using SSL anyway.

#####Arguments

  • usernameOrEmailstringrequired: the username or email.

  • passwordstringrequired: the password.

#####Returns

A promise which will be resolved with the logged user id if the login is successful. Otherwise it'll be rejected with an error.


###Asteroid.logout()

Logs out the user.

#####Arguments

None

#####Returns

A promise which will be resolved with if the logout is successful. Otherwise it'll be rejected with the error.


###Asteroid.subscribe(name, [param1, param2, ...])

Subscribes to the specified subscription. If an identical subscription (same name and parameters) has already been made, Asteroid will return that subscription.

#####Arguments

  • namestringrequired: the name of the subscription.

  • param1, param2, ...optional: a list of parameters that will be passed to the publish function on the server.

#####Returns

A subscription instance.


###Asteroid.Subscription

Subscription instances have the following properties:

  • idstring: the id of the subscription, as returned by the ddp.sub method

  • readypromise: a promise which will be resolved with the id of the subscription if the subscription succeeds (we receive the ddp ready message), or will be rejected if it fails (we receive, upon subscribing, the nosub message).

And the following method:

  • stop: it takes no argument, sends the ddp unsub message and deletes the subscription so it can be garbage collected.

###Asteroid.call(method, [param1, param2, ...])

Calls a server-side method with the specified arguments.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • param1, param2, ...optional: a list of parameters that will be passed to the method on the server.

#####Returns

An object with two properties: result and updated. Both properties are promises.

If the method is successful, the result promise will be resolved with the return value passed by the server. The updated promise will be resolved with nothing once the server emits the updated message, that tells the client that any side-effect that the method execution caused on the database has been reflected on the client (for example, if the method caused the insertion of an item into a collection, the client has been notified of said insertion).

If the method fails, the result promise will be rejected with the error returned by the server. The updated promise will be rejected as well (with nothing).


###Asteroid.apply(method, params)

Same as Asteroid.call, but using as array of parameters instead of a list.

#####Arguments

  • methodstringrequired: the name of the method to call.

  • paramsarrayoptional: an array of parameters that will be passed to the method on the server.

#####Returns

Same as Asteroid.call, see above.


###Asteroid.getCollection(name)

Creates and returns a collection. If the collection already exists, nothing changes and the existing one is returned.

#####Arguments

  • namestringrequired: the name of the collection to create.

#####Returns

A reference to the collection.

#####Note

Asteroid auto-creates collections for you. For example, if you subscribe to an hypothetical posts subscription, the server will start sending the client added messages that refer to items of the posts collection. With Meteor's front-end we would normally need to define the postscollection before we can access it.

With Asteroid, when the first added message is received, if the posts collection doesn't exist yet, it will get automatically created. We can then get a reference to that collection by calling createCollection (or by accessing the semi-private Asteroid.collections dictionary).

##Asteroid.Collection methods

All the following methods use latency compensation.

###Collection.insert(item)

Inserts an item into a collection. If the item does not have an _id property, one will be automatically generated for it.

#####Arguments

  • itemobjectrequired: the object to insert. Must be JSON serializable. Optional support for EJSON is planned.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the inserted item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which maybe should be fixed).

The remote promise is resolved with the _id of the inserted item if the remote insert is successful. Otherwise it's rejected with the reason of the failure.


###Collection.update(id, item)

Updates the specified item.

#####Arguments

  • idstringrequired: the id of the item to update.

  • itemobjectrequired: the object that will replace the old one.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the updated item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the updated item if the remote update is successful. Otherwise it's rejected with the reason of the failure.

#####Note

The API greatly differs from Meteor's API. Aligning the two is on the TODO list.


###Collection.remove(id)

Removes the specified item.

#####Arguments

  • idstringrequired: the id of the item to remove.

#####Returns

An object with two properties: local and remote. Both properties are promises.

The local promise is immediately resolved with the _id of the removed item. That is, unless an error occurred. In that case, an exception will be raised. (TODO: this is a bit of an API inconsistency which should be fixed).

The remote promise is resolved with the _id of the removed item if the remote remove is successful. Otherwise it's rejected with the reason of the failure.


###Collection.reactiveQuery(selector)

Gets a "reactive" subset of the collection.

#####Arguments

  • selectorobject or functionrequired: a MongoDB-style selector. Actually for now only a simple selector is supported (example {key1: val1, key2.subkey1: val2}). To compensate for this, you can also pass in a filter function which will be invoked on each item of the collection. If the function returns a truthy value, the item will be included, otherwise it will be left out. Help on adding support for more complex selectors is appreciated.

#####Returns

A ReactiveQuery instance.

##ReactiveQuery methods and properties

###ReactiveQuery.result

The array of items in the collection that matched the query.


###ReactiveQuery.on(event, handler)

Registers a handler for an event.

#####Arguments

  • eventstringrequired: the name of the event.

  • handlerfunctionrequired: the handler for the event.

Possible events are:

  • change: emitted whenever the result of the query changes. The id of the item that changed is passed to the handler.

About

An alternative client for a Meteor backend

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages