├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── TODO.md ├── exar-client ├── Cargo.toml ├── README.md └── src │ └── lib.rs ├── exar-core ├── Cargo.toml ├── README.md ├── benches │ └── database.rs ├── src │ ├── collection.rs │ ├── config.rs │ ├── connection.rs │ ├── database.rs │ ├── encoding.rs │ ├── error.rs │ ├── event.rs │ ├── lib.rs │ ├── log.rs │ ├── logger.rs │ ├── query.rs │ ├── routing_strategy.rs │ ├── scanner.rs │ ├── subscription.rs │ ├── util.rs │ └── validation.rs └── tests │ └── lib.rs ├── exar-db ├── Cargo.toml ├── README.md ├── log4rs.toml └── src │ ├── config.rs │ └── main.rs ├── exar-net ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ ├── message.rs │ └── stream.rs ├── exar-server ├── Cargo.toml ├── README.md └── src │ ├── config.rs │ ├── credentials.rs │ ├── handler.rs │ ├── lib.rs │ └── server.rs ├── exar-testkit ├── Cargo.toml └── src │ ├── collections.rs │ ├── encoding.rs │ ├── lib.rs │ └── net.rs ├── exar-ui ├── .gitignore ├── .npmignore ├── README.md ├── aurelia.protractor.js ├── build.sh ├── build │ ├── args.js │ ├── bundles.js │ ├── export.js │ ├── paths.js │ └── tasks │ │ ├── build.js │ │ ├── bundle.js │ │ ├── clean.js │ │ ├── dev.js │ │ ├── e2e.js │ │ ├── export-release.js │ │ ├── lint.js │ │ ├── prepare-release.js │ │ ├── serve.js │ │ ├── test.js │ │ └── watch.js ├── config.js ├── custom_typings │ ├── rx.lite.d.ts │ ├── tcp-socket.d.ts │ └── text-encoding.d.ts ├── gulpfile.js ├── index.html ├── index.js ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app.html │ ├── app.ts │ ├── components │ │ ├── dialogs │ │ │ ├── edit-connection.html │ │ │ └── edit-connection.ts │ │ └── layout │ │ │ ├── nav-bar.html │ │ │ └── nav-bar.ts │ ├── converters │ │ └── obfuscate.ts │ ├── exar │ │ ├── client.ts │ │ ├── model.ts │ │ └── net.ts │ ├── main.ts │ ├── models │ │ └── saved-connection.ts │ └── views │ │ ├── connection-handler.html │ │ ├── connection-handler.ts │ │ ├── home.html │ │ ├── home.ts │ │ ├── manage-connections.html │ │ └── manage-connections.ts ├── styles │ └── styles.css ├── test │ └── unit │ │ ├── app.spec.ts │ │ └── setup.ts ├── tsconfig.json ├── tsd.json ├── tslint.json ├── typings.json ├── typings │ ├── globals │ │ ├── angular-protractor │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── aurelia-protractor │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── bootstrap │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── jasmine │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── jquery │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── selenium-webdriver │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ ├── url │ │ │ ├── index.d.ts │ │ │ └── typings.json │ │ └── whatwg-fetch │ │ │ ├── index.d.ts │ │ │ └── typings.json │ ├── index.d.ts │ └── modules │ │ ├── aurelia-animator-css │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-binding │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-bootstrapper-webpack │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-bootstrapper │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-dependency-injection │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-dialog │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-event-aggregator │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-fetch-client │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-framework │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-history-browser │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-history │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-loader-webpack │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-loader │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-logging-console │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-logging │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-metadata │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-pal-browser │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-pal │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-path │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-polyfills │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-route-recognizer │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-router │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-task-queue │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-templating-binding │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-templating-resources │ │ ├── index.d.ts │ │ └── typings.json │ │ ├── aurelia-templating-router │ │ ├── index.d.ts │ │ └── typings.json │ │ └── aurelia-templating │ │ ├── index.d.ts │ │ └── typings.json └── wallaby.js └── test-all /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | target/ 12 | 13 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 14 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "exar-core", 4 | "exar-testkit", 5 | "exar-net", 6 | "exar-client", 7 | "exar-server", 8 | "exar-db" 9 | ] 10 | 11 | [profile.release] 12 | debug = true 13 | opt-level = 3 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exar DB 2 | 3 | An event store with streaming support, it uses flat-file based collections. 4 | 5 | ## Modules 6 | 7 | The database is split into the following modules: 8 | 9 | - [exar-core](https://github.com/bfil/exar-db/tree/master/exar-core): the core engine of Exar DB 10 | - [exar-net](https://github.com/bfil/exar-db/tree/master/exar-net): a TCP protocol for Exar DB 11 | - [exar-server](https://github.com/bfil/exar-db/tree/master/exar-server): a TCP server built on top of `exar-net` 12 | - [exar-client](https://github.com/bfil/exar-db/tree/master/exar-client): a TCP client built on top of `exar-net` 13 | - [exar-db](https://github.com/bfil/exar-db/tree/master/exar-db): the main executable of Exar DB 14 | 15 | ## Installation 16 | 17 | Install [`Cargo`](https://crates.io/install), then run: 18 | 19 | ``` 20 | cargo install exar-db 21 | ``` 22 | 23 | ## Starting the database 24 | 25 | Simply run `exar-db`. 26 | 27 | ## Configuring the database 28 | 29 | The database can be configured using a `TOML` configuration file, example below: 30 | 31 | ```toml 32 | log4rs_path = "/path/to/log4rs.toml" 33 | [database] 34 | logs_path = "~/exar-db/data" 35 | scanners = { nr_of_scanners = 2, sleep_time_in_ms = 10 } 36 | [database.collections.my-collection] 37 | routing_strategy = "Random" 38 | scanners = { nr_of_scanners = 4, sleep_time_in_ms = 5 } 39 | [server] 40 | host = "127.0.0.1" 41 | port = 38580 42 | username = "my-username" 43 | password = "my-secret" 44 | ``` 45 | 46 | Then run Exar DB by specifying the config file location: `exar-db --config=/path/to/config.toml`. 47 | 48 | For more information about the `database` and `server` configuration sections, 49 | check the documentation about 50 | [DatabaseConfig](https://bfil.github.io/exar-db/exar/struct.DatabaseConfig.html) and 51 | [ServerConfig](https://bfil.github.io/exar-db/exar_server/struct.ServerConfig.html). 52 | 53 | ## Logging 54 | 55 | Logging can be configured using a [log4rs](https://github.com/sfackler/log4rs) config file in `TOML` format, example below: 56 | 57 | ```toml 58 | [appenders.console] 59 | kind = "console" 60 | 61 | [appenders.console.encoder] 62 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 63 | 64 | [appenders.file] 65 | kind = "file" 66 | path = "exar-db.log" 67 | 68 | [appenders.file.encoder] 69 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 70 | 71 | [root] 72 | level = "info" 73 | appenders = ["console", "file"] 74 | ``` 75 | 76 | ## Interacting with the database from Rust 77 | 78 | To interact with the database from a rust application use [exar-client](https://github.com/bfil/exar-db/tree/master/exar-client). 79 | 80 | Basic connect/publish/subscribe examples are available at the [exar-client](https://bfil.github.io/exar-db/exar_client/index.html) section of the documentation. 81 | 82 | ## Interacting with the database via TCP 83 | 84 | To interact with the database a very simple TCP protocol can be used even via `telnet`. 85 | 86 | ``` 87 | telnet 127.0.0.1 38580 88 | ``` 89 | 90 | Once the TCP connection has been established, you can use the commands defined in the 91 | [exar-net](https://bfil.github.io/exar-db/exar_net/index.html) 92 | section of the documentation. 93 | 94 | ## Exar UI 95 | 96 | A simple user interface, built with Electron, useful to interact with the database is available [here](https://github.com/bfil/exar-db/tree/master/exar-ui), but it currently needs to be run from source. 97 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - Add Architecture Docs 2 | -------------------------------------------------------------------------------- /exar-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar-client" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Exar DB's TCP client" 6 | keywords = ["exar", "exar-db", "tcp", "client"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-client" 8 | documentation = "https://bfil.github.io/exar-db/exar_client/index.html" 9 | license = "AGPL-3.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | exar = { version = "0.1", path = "../exar-core" } 14 | exar-net = { version = "0.1", path = "../exar-net" } 15 | log = "0.3" 16 | 17 | [dev-dependencies] 18 | exar-testkit = { version = "0.1", path = "../exar-testkit" } 19 | -------------------------------------------------------------------------------- /exar-client/README.md: -------------------------------------------------------------------------------- 1 | # Exar DB's Client 2 | 3 | A client implementation that uses Exar DB's TCP protocol. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/exar-client.svg)](https://crates.io/crates/exar-client) 6 | 7 | [Documentation](https://bfil.github.io/exar-db/exar_client/index.html) 8 | -------------------------------------------------------------------------------- /exar-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Exar DB's core event store engine with streaming support" 6 | keywords = ["exar", "exar-db", "event", "store", "database"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-core" 8 | documentation = "https://bfil.github.io/exar-db/exar/index.html" 9 | license = "AGPL-3.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | indexed-line-reader = "0.2" 14 | log = "0.3" 15 | rand = "0.3" 16 | time = "0.1" 17 | rustc-serialize = { optional = true, version = "0.3" } 18 | serde = { optional = true, version = "0.9" } 19 | serde_derive = { optional = true, version = "0.9" } 20 | 21 | [features] 22 | rustc-serialization = ["rustc-serialize"] 23 | serde-serialization = ["serde", "serde_derive"] 24 | 25 | [dev-dependencies] 26 | exar-testkit = { version = "0.1", path = "../exar-testkit" } 27 | serde_json = "0.9" 28 | -------------------------------------------------------------------------------- /exar-core/README.md: -------------------------------------------------------------------------------- 1 | # Exar DB's Core 2 | 3 | An event store with streaming support, it uses flat-file based collections. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/exar.svg)](https://crates.io/crates/exar) 6 | 7 | [Documentation](https://bfil.github.io/exar-db/exar/index.html) 8 | -------------------------------------------------------------------------------- /exar-core/benches/database.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate exar; 4 | extern crate rand; 5 | extern crate test; 6 | 7 | #[cfg(test)] 8 | extern crate exar_testkit; 9 | 10 | use exar::*; 11 | use exar_testkit::*; 12 | use test::Bencher; 13 | 14 | #[bench] 15 | fn bench_publish(b: &mut Bencher) { 16 | let collection_name = &random_collection_name(); 17 | let config = DatabaseConfig::default(); 18 | let mut db = Database::new(config); 19 | let connection = db.connect(collection_name).unwrap(); 20 | b.iter(|| { 21 | let _ = connection.publish(Event::new("data", vec!["tag1"])); 22 | }); 23 | assert!(db.drop_collection(collection_name).is_ok()); 24 | connection.close(); 25 | } 26 | -------------------------------------------------------------------------------- /exar-core/src/connection.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::sync::{Arc, Mutex}; 4 | 5 | /// Exar DB's database connection, which contains a reference to a collection wrapped into an Arc/Mutex. 6 | /// It allows publishing and subscribing to the underling collection of events. 7 | /// 8 | /// # Examples 9 | /// ```no_run 10 | /// extern crate exar; 11 | /// 12 | /// # fn main() { 13 | /// use exar::*; 14 | /// use std::sync::{Arc, Mutex}; 15 | /// 16 | /// let collection_name = "test"; 17 | /// let collection_config = CollectionConfig::default(); 18 | /// let collection = Collection::new(collection_name, &collection_config).unwrap(); 19 | /// let connection = Connection::new(Arc::new(Mutex::new(collection))); 20 | /// # } 21 | /// ``` 22 | #[derive(Clone, Debug)] 23 | pub struct Connection { 24 | collection: Arc> 25 | } 26 | 27 | impl Connection { 28 | /// Creates a new instance of a connection with the given collection. 29 | pub fn new(collection: Arc>) -> Connection { 30 | Connection { 31 | collection: collection 32 | } 33 | } 34 | 35 | /// Publishes an event into the underlying collection and returns the `id` for the event created 36 | /// or a `DatabaseError` if a failure occurs. 37 | pub fn publish(&self, event: Event) -> Result { 38 | self.collection.lock().unwrap().publish(event) 39 | } 40 | 41 | /// Subscribes to the underlying collection of events using the given query and returns an event stream 42 | /// or a `DatabaseError` if a failure occurs. 43 | pub fn subscribe(&self, query: Query) -> Result { 44 | self.collection.lock().unwrap().subscribe(query) 45 | } 46 | 47 | /// Closes the connection. 48 | pub fn close(self) { 49 | drop(self) 50 | } 51 | } 52 | 53 | #[cfg(test)] 54 | mod tests { 55 | use super::super::*; 56 | use exar_testkit::*; 57 | 58 | #[test] 59 | fn test_connection() { 60 | let mut db = Database::new(DatabaseConfig::default()); 61 | 62 | let ref collection_name = random_collection_name(); 63 | let collection = db.get_collection(collection_name).expect("Unable to get collection"); 64 | 65 | let connection = Connection::new(collection); 66 | 67 | let test_event = Event::new("data", vec!["tag1", "tag2"]); 68 | assert_eq!(connection.publish(test_event.clone()), Ok(1)); 69 | 70 | let query = Query::current(); 71 | let retrieved_events: Vec<_> = connection.subscribe(query).unwrap().take(1).collect(); 72 | let expected_event = test_event.clone().with_id(1).with_timestamp(retrieved_events[0].timestamp); 73 | assert_eq!(retrieved_events, vec![expected_event]); 74 | 75 | connection.close(); 76 | 77 | assert!(db.drop_collection(collection_name).is_ok()); 78 | assert!(!db.contains_collection(collection_name)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /exar-core/src/database.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::collections::HashMap; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | /// Exar DB's main component, containing the database configuration and the references to the 7 | /// collections of events created. It is used to create new connections. 8 | /// 9 | /// # Examples 10 | /// ```no_run 11 | /// extern crate exar; 12 | /// 13 | /// # fn main() { 14 | /// use exar::*; 15 | /// 16 | /// let config = DatabaseConfig::default(); 17 | /// let mut db = Database::new(config); 18 | /// 19 | /// let collection_name = "test"; 20 | /// let connection = db.connect(collection_name).unwrap(); 21 | /// # } 22 | /// ``` 23 | #[derive(Clone, Debug)] 24 | pub struct Database { 25 | config: DatabaseConfig, 26 | collections: HashMap>> 27 | } 28 | 29 | impl Database { 30 | /// Creates a new instance of the database with the given configuration. 31 | pub fn new(config: DatabaseConfig) -> Database { 32 | Database { 33 | config: config, 34 | collections: HashMap::new() 35 | } 36 | } 37 | 38 | /// Returns a connection instance with the given name or a `DatabaseError` if a failure occurs. 39 | pub fn connect(&mut self, collection_name: &str) -> Result { 40 | match self.get_collection(collection_name) { 41 | Ok(collection) => Ok(Connection::new(collection)), 42 | Err(err) => Err(err) 43 | } 44 | } 45 | 46 | /// Returns an existing collection instance with the given name wrapped into an `Arc`/`Mutex` 47 | /// or a `DatabaseError` if a failure occurs, it creates a new collection if it does not exist. 48 | pub fn get_collection(&mut self, collection_name: &str) -> Result>, DatabaseError> { 49 | if !self.contains_collection(collection_name) { 50 | self.create_collection(collection_name) 51 | } else { 52 | match self.collections.get(collection_name) { 53 | Some(collection) => Ok(collection.clone()), 54 | None => unreachable!() 55 | } 56 | } 57 | } 58 | 59 | /// Creates and returns a new collection instance with the given name wrapped into an `Arc`/`Mutex` 60 | /// or a `DatabaseError` if a failure occurs. 61 | pub fn create_collection(&mut self, collection_name: &str) -> Result>, DatabaseError> { 62 | let collection_config = self.config.collection_config(collection_name); 63 | Collection::new(collection_name, &collection_config).and_then(|collection| { 64 | let collection = Arc::new(Mutex::new(collection)); 65 | self.collections.insert(collection_name.to_owned(), collection.clone()); 66 | Ok(collection) 67 | }) 68 | } 69 | 70 | /// Drops the collection with the given name or returns an error if a failure occurs. 71 | pub fn drop_collection(&mut self, collection_name: &str) -> Result<(), DatabaseError> { 72 | self.get_collection(collection_name).and_then(|collection| { 73 | (*collection.lock().unwrap()).drop().and_then(|_| { 74 | self.collections.remove(collection_name); 75 | Ok(()) 76 | }) 77 | }) 78 | } 79 | 80 | /// Returns wether a collection with the given name exists. 81 | pub fn contains_collection(&self, collection_name: &str) -> bool { 82 | self.collections.contains_key(collection_name) 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::super::*; 89 | use exar_testkit::*; 90 | 91 | #[test] 92 | fn test_constructor() { 93 | let db = Database::new(DatabaseConfig::default()); 94 | 95 | assert_eq!(db.config, DatabaseConfig::default()); 96 | assert_eq!(db.collections.len(), 0); 97 | } 98 | 99 | #[test] 100 | fn test_connect() { 101 | let mut db = Database::new(DatabaseConfig::default()); 102 | 103 | let ref collection_name = random_collection_name(); 104 | assert!(db.connect(collection_name).is_ok()); 105 | assert!(db.contains_collection(collection_name)); 106 | assert!(db.drop_collection(collection_name).is_ok()); 107 | } 108 | 109 | #[test] 110 | fn test_connection_failure() { 111 | let mut db = Database::new(DatabaseConfig::default()); 112 | 113 | let ref collection_name = invalid_collection_name(); 114 | assert!(db.connect(collection_name).is_err()); 115 | assert!(!db.contains_collection(collection_name)); 116 | assert!(db.drop_collection(collection_name).is_err()); 117 | } 118 | 119 | #[test] 120 | fn test_collection_management() { 121 | let mut db = Database::new(DatabaseConfig::default()); 122 | 123 | let ref collection_name = random_collection_name(); 124 | assert!(!db.contains_collection(collection_name)); 125 | assert!(db.get_collection(collection_name).is_ok()); 126 | assert!(db.contains_collection(collection_name)); 127 | assert_eq!(db.collections.len(), 1); 128 | 129 | assert!(db.get_collection(collection_name).is_ok()); 130 | assert!(db.contains_collection(collection_name)); 131 | assert_eq!(db.collections.len(), 1); 132 | 133 | assert!(db.drop_collection(collection_name).is_ok()); 134 | assert!(!db.contains_collection(collection_name)); 135 | assert_eq!(db.collections.len(), 0); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /exar-core/src/encoding.rs: -------------------------------------------------------------------------------- 1 | #![macro_use] 2 | 3 | use std::fmt::{Debug, Display, Formatter, Result as DisplayResult}; 4 | use std::str::{FromStr, SplitN}; 5 | 6 | /// Generates a tab separated string from a list of string slices 7 | /// 8 | /// # Examples 9 | /// ``` 10 | /// #[macro_use] 11 | /// extern crate exar; 12 | /// 13 | /// # fn main() { 14 | /// let tab_separated_value = tab_separated!("hello", "world"); 15 | /// # } 16 | /// ``` 17 | #[macro_export] 18 | macro_rules! tab_separated { 19 | ($($x:expr),*) => ({ 20 | let vec: Vec = vec![$($x.to_string()),*]; 21 | vec.join("\t") 22 | }) 23 | } 24 | 25 | /// A trait for serializing a type to a tab-separated string. 26 | pub trait ToTabSeparatedString { 27 | /// Returns a tab-separated string from the value. 28 | fn to_tab_separated_string(&self) -> String; 29 | } 30 | 31 | /// A trait for deserializing a type from a tab-separated string slice. 32 | pub trait FromTabSeparatedStr { 33 | /// Returns an instance of `Self` from a tab-separated string slice 34 | /// or a `ParseError` if a failure occurs while parsing the string. 35 | fn from_tab_separated_str(s: &str) -> Result where Self: Sized; 36 | } 37 | 38 | /// A list specifying categories of parse error. 39 | #[derive(Clone, Debug, PartialEq, Eq)] 40 | pub enum ParseError { 41 | /// The parsing failed because of the given reason. 42 | ParseError(String), 43 | /// The parsing failed because of a missing field at the given position. 44 | MissingField(usize) 45 | } 46 | 47 | impl Display for ParseError { 48 | fn fmt(&self, f: &mut Formatter) -> DisplayResult { 49 | match *self { 50 | ParseError::ParseError(ref description) => write!(f, "{}", description), 51 | ParseError::MissingField(index) => write!(f, "missing field at index {}", index) 52 | } 53 | } 54 | } 55 | 56 | /// A parser for tab-separated strings 57 | /// 58 | /// # Examples 59 | /// ``` 60 | /// #[macro_use] 61 | /// extern crate exar; 62 | /// 63 | /// # fn main() { 64 | /// use exar::*; 65 | /// 66 | /// let tab_separated_value = tab_separated!("hello", "world"); 67 | /// let mut parser = TabSeparatedParser::new(2, &tab_separated_value); 68 | /// 69 | /// let hello: String = parser.parse_next().unwrap(); 70 | /// let world: String = parser.parse_next().unwrap(); 71 | /// # } 72 | /// ``` 73 | pub struct TabSeparatedParser<'a> { 74 | index: usize, 75 | parts: SplitN<'a, &'a str> 76 | } 77 | 78 | impl<'a> TabSeparatedParser<'a> { 79 | /// Creates a new parser that splits a string up to `n` parts. 80 | pub fn new(n: usize, s: &'a str) -> TabSeparatedParser<'a> { 81 | TabSeparatedParser { 82 | index: 0, 83 | parts: s.splitn(n, "\t") 84 | } 85 | } 86 | 87 | /// Parses the next string slice into the given type `T` and returns it, 88 | /// or returns a `ParseError` if a failure occurs while parsing the value. 89 | pub fn parse_next(&mut self) -> Result where T: FromStr, ::Err: Display + Debug { 90 | match self.parts.next().map(|x| x.parse()) { 91 | Some(Ok(value)) => { 92 | self.index += 1; 93 | Ok(value) 94 | }, 95 | Some(Err(err)) => Err(ParseError::ParseError(format!("{}", err))), 96 | None => Err(ParseError::MissingField(self.index)) 97 | } 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests { 103 | use super::super::*; 104 | 105 | #[test] 106 | fn test_tab_separated_macro() { 107 | let tab_separated_value = tab_separated!("hello", "world", "!"); 108 | assert_eq!(tab_separated_value, "hello\tworld\t!"); 109 | 110 | let tab_separated_value = tab_separated!(1, 2); 111 | assert_eq!(tab_separated_value, "1\t2"); 112 | } 113 | 114 | #[test] 115 | fn test_tab_separated_parser() { 116 | let tab_separated_value = tab_separated!("hello", "world", "!"); 117 | let mut parser = TabSeparatedParser::new(3, &tab_separated_value); 118 | 119 | let hello: String = parser.parse_next().expect("Unable to parse value"); 120 | let world: String = parser.parse_next().expect("Unable to parse value"); 121 | let exclamation_mark: String = parser.parse_next().expect("Unable to parse value"); 122 | 123 | assert_eq!(hello, "hello".to_owned()); 124 | assert_eq!(world, "world".to_owned()); 125 | assert_eq!(exclamation_mark, "!".to_owned()); 126 | 127 | let tab_separated_value = tab_separated!(1, 2); 128 | let mut parser = TabSeparatedParser::new(2, &tab_separated_value); 129 | 130 | let one: u8 = parser.parse_next().expect("Unable to parse value"); 131 | let two: u8 = parser.parse_next().expect("Unable to parse value"); 132 | 133 | assert_eq!(one, 1); 134 | assert_eq!(two, 2); 135 | } 136 | 137 | #[test] 138 | fn test_parse_error() { 139 | let tab_separated_value = tab_separated!("hello", "world"); 140 | let mut parser = TabSeparatedParser::new(2, &tab_separated_value); 141 | 142 | assert_eq!(parser.parse_next::(), Err(ParseError::ParseError("invalid digit found in string".to_owned()))); 143 | } 144 | 145 | #[test] 146 | fn test_missing_field_error() { 147 | let tab_separated_value = tab_separated!("hello", "world"); 148 | let mut parser = TabSeparatedParser::new(2, &tab_separated_value); 149 | 150 | let hello: String = parser.parse_next().expect("Unable to parse value"); 151 | let world: String = parser.parse_next().expect("Unable to parse value"); 152 | 153 | assert_eq!(hello, "hello".to_owned()); 154 | assert_eq!(world, "world".to_owned()); 155 | 156 | assert_eq!(parser.parse_next::(), Err(ParseError::MissingField(2))); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /exar-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Exar DB 2 | //! Exar DB is an event store with streaming support 3 | //! which uses a flat-file for each collection of events 4 | //! 5 | //! ## Database Initialization 6 | //! ``` 7 | //! extern crate exar; 8 | //! 9 | //! # fn main() { 10 | //! use exar::*; 11 | //! 12 | //! let config = DatabaseConfig::default(); 13 | //! let mut db = Database::new(config); 14 | //! # } 15 | //! ``` 16 | //! ## Publishing events 17 | //! ```no_run 18 | //! extern crate exar; 19 | //! 20 | //! # fn main() { 21 | //! use exar::*; 22 | //! 23 | //! let config = DatabaseConfig::default(); 24 | //! let mut db = Database::new(config); 25 | //! 26 | //! let collection_name = "test"; 27 | //! let connection = db.connect(collection_name).unwrap(); 28 | //! 29 | //! match connection.publish(Event::new("payload", vec!["tag1", "tag2"])) { 30 | //! Ok(event_id) => println!("Published event with ID: {}", event_id), 31 | //! Err(err) => panic!("Unable to publish event: {}", err) 32 | //! }; 33 | //! # } 34 | //! ``` 35 | //! ## Querying events 36 | //! ```no_run 37 | //! extern crate exar; 38 | //! 39 | //! # fn main() { 40 | //! use exar::*; 41 | //! 42 | //! let config = DatabaseConfig::default(); 43 | //! let mut db = Database::new(config); 44 | //! 45 | //! let collection_name = "test"; 46 | //! let connection = db.connect(collection_name).unwrap(); 47 | //! 48 | //! let query = Query::live().offset(0).limit(10).by_tag("tag1"); 49 | //! let event_stream = connection.subscribe(query).unwrap(); 50 | //! for event in event_stream { 51 | //! println!("Received event: {}", event); 52 | //! } 53 | //! # } 54 | //! ``` 55 | 56 | #[cfg(feature = "rustc-serialization")] extern crate rustc_serialize; 57 | #[cfg(feature = "serde-serialization")] extern crate serde; 58 | #[cfg(feature = "serde-serialization")] #[macro_use] extern crate serde_derive; 59 | 60 | #[cfg(test)] #[macro_use] 61 | extern crate exar_testkit; 62 | 63 | #[macro_use] 64 | extern crate log as logging; 65 | 66 | extern crate indexed_line_reader; 67 | extern crate rand; 68 | extern crate time; 69 | 70 | mod logger; 71 | mod config; 72 | mod collection; 73 | mod connection; 74 | mod database; 75 | mod encoding; 76 | mod error; 77 | mod event; 78 | mod log; 79 | mod query; 80 | mod scanner; 81 | mod routing_strategy; 82 | mod subscription; 83 | mod util; 84 | mod validation; 85 | 86 | pub use self::logger::*; 87 | pub use self::config::*; 88 | pub use self::collection::*; 89 | pub use self::connection::*; 90 | pub use self::database::*; 91 | pub use self::encoding::*; 92 | pub use self::error::*; 93 | pub use self::event::*; 94 | pub use self::log::*; 95 | pub use self::query::*; 96 | pub use self::routing_strategy::*; 97 | pub use self::scanner::*; 98 | pub use self::subscription::*; 99 | pub use self::util::*; 100 | pub use self::validation::*; 101 | -------------------------------------------------------------------------------- /exar-core/src/query.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// Exar DB's subscription query. 4 | /// 5 | /// # Examples 6 | /// ``` 7 | /// extern crate exar; 8 | /// 9 | /// # fn main() { 10 | /// use exar::*; 11 | /// 12 | /// let query = Query::new(true, 100, Some(20), Some("tag".to_owned())); 13 | /// 14 | /// // or using the fluent API 15 | /// let fluent_query = Query::live().offset(100).limit(20).by_tag("tag"); 16 | /// # } 17 | /// ``` 18 | #[derive(Clone, Debug, PartialEq, Eq)] 19 | pub struct Query { 20 | /// Indicates wether the query targets real-time events. 21 | pub live_stream: bool, 22 | /// Indicates the query target offset. 23 | pub offset: u64, 24 | /// Indicates the maximum number of events to be returned by the query, if specified. 25 | pub limit: Option, 26 | /// Indicates the query target event tag, if specified. 27 | pub tag: Option, 28 | position: u64, 29 | count: u64 30 | } 31 | 32 | impl Query { 33 | /// Creates a new `Query` from the given parameters. 34 | pub fn new(live_stream: bool, offset: u64, limit: Option, tag: Option) -> Query { 35 | Query { 36 | offset: offset, 37 | limit: limit, 38 | tag: tag, 39 | live_stream: live_stream, 40 | position: offset, 41 | count: 0 42 | } 43 | } 44 | 45 | /// Initializes a `Query` targeting the current events in the event log. 46 | pub fn current() -> Query { 47 | Query::new(false, 0, None, None) 48 | } 49 | 50 | /// Initializes a `Query` targeting the current and real-time events in the event log. 51 | pub fn live() -> Query { 52 | Query::new(true, 0, None, None) 53 | } 54 | 55 | /// Mutates and returns the query by updating its target offset. 56 | pub fn offset(mut self, offset: u64) -> Query { 57 | self.offset = offset; 58 | self.position = offset; 59 | self 60 | } 61 | 62 | /// Mutates and returns the query by updating its limit. 63 | pub fn limit(mut self, limit: u64) -> Query { 64 | self.limit = Some(limit); 65 | self 66 | } 67 | 68 | /// Mutates and returns the query by updating its target event tag. 69 | pub fn by_tag(mut self, tag: &str) -> Query { 70 | self.tag = Some(tag.to_owned()); 71 | self 72 | } 73 | 74 | /// Returns wether a given `Event` matches the query. 75 | pub fn matches(&self, event: &Event) -> bool { 76 | match self.tag { 77 | Some(ref tag) => self.position < event.id && event.tags.contains(tag), 78 | None => self.position < event.id 79 | } 80 | } 81 | 82 | /// Returns wether the query is still active. 83 | pub fn is_active(&self) -> bool { 84 | match self.limit { 85 | Some(limit) => self.count < limit, 86 | None => true 87 | } 88 | } 89 | 90 | /// Updates the internal state of the query given the last matching event `id`. 91 | pub fn update(&mut self, event_id: u64) { 92 | self.position = event_id; 93 | self.count += 1; 94 | } 95 | 96 | /// Returns the offsets interval the query targets. 97 | pub fn interval(&self) -> Interval { 98 | let start = self.position; 99 | let end = if self.limit.is_none() || self.tag.is_some() { 100 | u64::max_value() 101 | } else { 102 | start + self.limit.unwrap() 103 | }; 104 | Interval::new(start, end) 105 | } 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::super::*; 111 | 112 | #[test] 113 | fn test_constructors_and_modifiers() { 114 | let query = Query::new(true, 100, Some(20), Some("tag".to_owned())); 115 | assert_eq!(query.live_stream, true); 116 | assert_eq!(query.offset, 100); 117 | assert_eq!(query.limit, Some(20)); 118 | assert_eq!(query.tag, Some("tag".to_owned())); 119 | 120 | let query = Query::current(); 121 | assert_eq!(query.live_stream, false); 122 | assert_eq!(query.offset, 0); 123 | assert_eq!(query.limit, None); 124 | assert_eq!(query.tag, None); 125 | 126 | let query = Query::live(); 127 | assert_eq!(query.live_stream, true); 128 | assert_eq!(query.offset, 0); 129 | assert_eq!(query.limit, None); 130 | assert_eq!(query.tag, None); 131 | 132 | let query = query.offset(100); 133 | assert_eq!(query.offset, 100); 134 | 135 | let query = query.limit(20); 136 | assert_eq!(query.limit, Some(20)); 137 | 138 | let query = query.by_tag("tag"); 139 | assert_eq!(query.tag, Some("tag".to_owned())); 140 | } 141 | 142 | #[test] 143 | fn test_event_matching() { 144 | let mut query = Query::current(); 145 | 146 | assert!(query.matches(&Event::new("data", vec!["tag1"]).with_id(1))); 147 | 148 | query.update(1); 149 | 150 | assert!(!query.matches(&Event::new("data", vec!["tag1"]).with_id(1))); 151 | 152 | let mut query = Query::current().by_tag("tag1"); 153 | 154 | assert!(query.matches(&Event::new("data", vec!["tag1"]).with_id(1))); 155 | assert!(!query.matches(&Event::new("data", vec!["tag2"]).with_id(1))); 156 | 157 | query.update(1); 158 | 159 | assert!(!query.matches(&Event::new("data", vec!["tag1"]).with_id(1))); 160 | } 161 | 162 | #[test] 163 | fn test_is_active() { 164 | let query = Query::current(); 165 | 166 | assert!(query.is_active()); 167 | 168 | let mut query = query.limit(3); 169 | 170 | assert!(query.is_active()); 171 | 172 | query.update(1); 173 | query.update(2); 174 | query.update(3); 175 | 176 | assert_eq!(query.position, 3); 177 | assert_eq!(query.count, 3); 178 | assert!(!query.is_active()); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /exar-core/src/routing_strategy.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rustc-serialization")] use rustc_serialize::{Encoder, Encodable, Decoder, Decodable}; 2 | #[cfg(feature = "serde-serialization")] use serde::{Serialize, Serializer, Deserialize, Deserializer}; 3 | #[cfg(feature = "serde-serialization")] use serde::de::{Error, Visitor}; 4 | #[cfg(feature = "serde-serialization")] use std::fmt; 5 | 6 | /// A list specifying categories of routing strategy. 7 | #[derive(Clone, Debug, PartialEq, Eq)] 8 | pub enum RoutingStrategy { 9 | /// The next element is picked at random. 10 | Random, 11 | /// The next element is picked using the round-robin algorithm. 12 | RoundRobin(usize) 13 | } 14 | 15 | #[cfg(feature = "rustc-serialization")] 16 | impl Encodable for RoutingStrategy { 17 | fn encode(&self, s: &mut S) -> Result<(), S::Error> { 18 | match *self { 19 | RoutingStrategy::Random => s.emit_str("Random"), 20 | RoutingStrategy::RoundRobin(_) => s.emit_str("RoundRobin") 21 | } 22 | } 23 | } 24 | 25 | #[cfg(feature = "rustc-serialization")] 26 | impl Decodable for RoutingStrategy { 27 | fn decode(d: &mut D) -> Result { 28 | d.read_str().map(|s| { 29 | match s.as_ref() { 30 | "Random" => RoutingStrategy::Random, 31 | "RoundRobin" => RoutingStrategy::RoundRobin(0), 32 | _ => RoutingStrategy::default() 33 | } 34 | }) 35 | } 36 | } 37 | 38 | #[cfg(feature = "serde-serialization")] 39 | impl Serialize for RoutingStrategy { 40 | fn serialize(&self, serializer: S) -> Result { 41 | match *self { 42 | RoutingStrategy::Random => serializer.serialize_str("Random"), 43 | RoutingStrategy::RoundRobin(_) => serializer.serialize_str("RoundRobin") 44 | } 45 | } 46 | } 47 | 48 | #[cfg(feature = "serde-serialization")] 49 | impl Deserialize for RoutingStrategy { 50 | fn deserialize(deserializer: D) -> Result { 51 | deserializer.deserialize_str(RoutingStrategyVisitor) 52 | } 53 | } 54 | 55 | #[cfg(feature = "serde-serialization")] 56 | struct RoutingStrategyVisitor; 57 | 58 | #[cfg(feature = "serde-serialization")] 59 | impl Visitor for RoutingStrategyVisitor { 60 | type Value = RoutingStrategy; 61 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 62 | formatter.write_str("Random or RoundRobin") 63 | } 64 | fn visit_str(self, s: &str) -> Result { 65 | match s { 66 | "Random" => Ok(RoutingStrategy::Random), 67 | "RoundRobin" => Ok(RoutingStrategy::RoundRobin(0)), 68 | _ => Ok(RoutingStrategy::default()) 69 | } 70 | } 71 | } 72 | 73 | impl Default for RoutingStrategy { 74 | fn default() -> Self { 75 | RoutingStrategy::RoundRobin(0) 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use super::super::*; 82 | 83 | #[cfg(feature = "rustc-serialization")] 84 | use rustc_serialize::json; 85 | 86 | #[cfg(feature = "serde-serialization")] 87 | extern crate serde_json; 88 | 89 | #[test] 90 | fn test_default() { 91 | assert_eq!(RoutingStrategy::default(), RoutingStrategy::RoundRobin(0)); 92 | } 93 | 94 | #[test] 95 | #[cfg(feature = "rustc-serialization")] 96 | fn test_rustc_serialization() { 97 | let routing_strategy = RoutingStrategy::Random; 98 | assert_eq!(json::encode(&routing_strategy).unwrap(), "\"Random\""); 99 | assert_eq!(json::decode::("\"Random\"").unwrap(), routing_strategy); 100 | 101 | let routing_strategy = RoutingStrategy::RoundRobin(0); 102 | assert_eq!(json::encode(&routing_strategy).unwrap(), "\"RoundRobin\""); 103 | assert_eq!(json::decode::("\"RoundRobin\"").unwrap(), routing_strategy); 104 | } 105 | 106 | #[test] 107 | #[cfg(feature = "serde-serialization")] 108 | fn test_serde_serialization() { 109 | let routing_strategy = RoutingStrategy::Random; 110 | assert_eq!(serde_json::to_string(&routing_strategy).unwrap(), "\"Random\""); 111 | assert_eq!(serde_json::from_str::("\"Random\"").unwrap(), routing_strategy); 112 | 113 | let routing_strategy = RoutingStrategy::RoundRobin(0); 114 | assert_eq!(serde_json::to_string(&routing_strategy).unwrap(), "\"RoundRobin\""); 115 | assert_eq!(serde_json::from_str::("\"RoundRobin\"").unwrap(), routing_strategy); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /exar-core/src/subscription.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | use std::sync::mpsc::Sender; 4 | 5 | /// Exar DB's subscription. 6 | /// 7 | /// # Examples 8 | /// ``` 9 | /// extern crate exar; 10 | /// 11 | /// # fn main() { 12 | /// use exar::*; 13 | /// use std::sync::mpsc::channel; 14 | /// 15 | /// let (sender, receiver) = channel(); 16 | /// let event = Event::new("data", vec!["tag1", "tag2"]); 17 | /// 18 | /// let mut subscription = Subscription::new(sender, Query::current()); 19 | /// subscription.send(event).unwrap(); 20 | /// let event_stream_message = receiver.recv().unwrap(); 21 | /// # } 22 | /// ``` 23 | #[derive(Clone, Debug)] 24 | pub struct Subscription { 25 | active: bool, 26 | /// The channel sender used to stream `EventStreamMessage`s back to the subscriber. 27 | pub event_stream_sender: Sender, 28 | /// The query associated to this subscription. 29 | pub query: Query 30 | } 31 | 32 | impl Subscription { 33 | /// Creates a new `Subscription` with the given channel sender and query. 34 | pub fn new(sender: Sender, query: Query) -> Subscription { 35 | Subscription { 36 | active: true, 37 | event_stream_sender: sender, 38 | query: query 39 | } 40 | } 41 | 42 | /// Sends an `Event` to the subscriber or returns a `DatabaseError` if a failure occurs. 43 | pub fn send(&mut self, event: Event) -> Result<(), DatabaseError> { 44 | let event_id = event.id; 45 | match self.event_stream_sender.send(EventStreamMessage::Event(event)) { 46 | Ok(_) => { 47 | self.query.update(event_id); 48 | if !self.is_active() || !self.query.is_active() { 49 | self.active = false; 50 | match self.event_stream_sender.send(EventStreamMessage::End) { 51 | Ok(_) => Ok(()), 52 | Err(_) => Err(DatabaseError::EventStreamError(EventStreamError::Closed)) 53 | } 54 | } else { 55 | Ok(()) 56 | } 57 | }, 58 | Err(_) => { 59 | self.active = false; 60 | Err(DatabaseError::EventStreamError(EventStreamError::Closed)) 61 | } 62 | } 63 | } 64 | 65 | /// Returns wether the subscription is still active. 66 | pub fn is_active(&self) -> bool { 67 | self.active 68 | } 69 | 70 | /// Returns wether the subscription is interested in the given `Event`. 71 | pub fn matches_event(&self, event: &Event) -> bool { 72 | self.is_active() && self.query.is_active() && self.query.matches(event) 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::super::*; 79 | 80 | use std::sync::mpsc::channel; 81 | 82 | #[test] 83 | fn test_simple_subscription() { 84 | let (sender, receiver) = channel(); 85 | let event = Event::new("data", vec!["tag1", "tag2"]).with_id(1); 86 | 87 | let mut subscription = Subscription::new(sender, Query::current()); 88 | 89 | assert!(subscription.send(event.clone()).is_ok()); 90 | assert_eq!(receiver.recv(), Ok(EventStreamMessage::Event(event.clone()))); 91 | assert_eq!(subscription.query.interval().start, 1); 92 | assert!(subscription.is_active()); 93 | 94 | drop(receiver); 95 | 96 | assert_eq!(subscription.send(event.clone()), Err(DatabaseError::EventStreamError(EventStreamError::Closed))); 97 | assert_eq!(subscription.query.interval().start, 1); 98 | assert!(!subscription.is_active()); 99 | } 100 | 101 | #[test] 102 | fn test_subscription_event_stream_end() { 103 | let (sender, receiver) = channel(); 104 | let event = Event::new("data", vec!["tag1", "tag2"]).with_id(1); 105 | 106 | let mut subscription = Subscription::new(sender, Query::current().limit(1)); 107 | 108 | assert!(subscription.send(event.clone()).is_ok()); 109 | assert_eq!(receiver.recv(), Ok(EventStreamMessage::Event(event.clone()))); 110 | assert_eq!(receiver.recv(), Ok(EventStreamMessage::End)); 111 | assert_eq!(subscription.query.interval().start, 1); 112 | assert!(!subscription.is_active()); 113 | 114 | drop(receiver); 115 | 116 | assert_eq!(subscription.send(event.clone()), Err(DatabaseError::EventStreamError(EventStreamError::Closed))); 117 | assert_eq!(subscription.query.interval().start, 1); 118 | assert!(!subscription.is_active()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /exar-core/src/util.rs: -------------------------------------------------------------------------------- 1 | use std::io::prelude::*; 2 | use std::io::{BufWriter, Result}; 3 | 4 | /// A trait for writing a line into a stream. 5 | pub trait WriteLine { 6 | /// Writes a string slice into this writer by appending a new line at the end of it, 7 | /// returning whether the write succeeded. 8 | fn write_line(&mut self, line: &str) -> Result; 9 | } 10 | 11 | impl WriteLine for BufWriter { 12 | fn write_line(&mut self, line: &str) -> Result { 13 | self.write(format!("{}\n", line).as_bytes()).and_then(|bytes_written| { 14 | self.flush().and_then(|_| { 15 | Ok(bytes_written) 16 | }) 17 | }) 18 | } 19 | } 20 | 21 | /// An interval. 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | pub struct Interval { 24 | /// The start of the interval. 25 | pub start: T, 26 | /// The end of the interval. 27 | pub end: T 28 | } 29 | 30 | /// A trait for merging a type. 31 | pub trait Merge: Sized { 32 | fn merge(&mut self); 33 | fn merged(mut self) -> Self { 34 | self.merge(); 35 | self 36 | } 37 | } 38 | 39 | impl Interval { 40 | pub fn new(start: T, end: T) -> Interval { 41 | Interval { 42 | start: start, 43 | end: end 44 | } 45 | } 46 | } 47 | 48 | impl Merge for Vec> { 49 | fn merge(&mut self) { 50 | if !self.is_empty() { 51 | self.sort_by(|a, b| a.start.cmp(&b.start)); 52 | let mut merged_intervals = vec![ self[0].clone() ]; 53 | for interval in self.iter().skip(1) { 54 | let last_pos = merged_intervals.len() - 1; 55 | if merged_intervals[last_pos].end < interval.start { 56 | merged_intervals.push(interval.clone()); 57 | } else if merged_intervals[last_pos].end >= interval.start && 58 | merged_intervals[last_pos].end <= interval.end { 59 | merged_intervals[last_pos].end = interval.end; 60 | } 61 | } 62 | *self = merged_intervals; 63 | } 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use super::super::*; 70 | 71 | use std::fs::*; 72 | use std::io::{BufRead, BufReader, BufWriter, Seek, SeekFrom}; 73 | 74 | #[test] 75 | fn test_write_line() { 76 | let file = OpenOptions::new().read(true).write(true).create(true) 77 | .open("buf-writer.log").expect("Unable to create file"); 78 | 79 | let mut buf_writer = BufWriter::new(file); 80 | assert!(buf_writer.write_line("line 1").is_ok()); 81 | assert!(buf_writer.write_line("line 2").is_ok()); 82 | 83 | let mut file = buf_writer.into_inner().expect("Unable to extract inner context"); 84 | file.seek(SeekFrom::Start(0)).expect("Unable to seek to start"); 85 | let mut lines = BufReader::new(file).lines(); 86 | 87 | let line = lines.next().expect("Unable to read next line") 88 | .expect("Unable to read next line"); 89 | 90 | assert_eq!(line, "line 1".to_owned()); 91 | 92 | let line = lines.next().expect("Unable to read next line") 93 | .expect("Unable to read next line"); 94 | 95 | assert_eq!(line, "line 2".to_owned()); 96 | 97 | assert!(remove_file("buf-writer.log").is_ok()); 98 | } 99 | 100 | #[test] 101 | fn test_intervals_merging() { 102 | let intervals = vec![]; 103 | assert_eq!(intervals.merged(), vec![]); 104 | 105 | let intervals = vec![ 106 | Interval::new(0, 10), 107 | Interval::new(30, 50), 108 | Interval::new(40, 70) 109 | ]; 110 | assert_eq!(intervals.merged(), vec![Interval::new(0, 10), Interval::new(30, 70)]); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /exar-core/src/validation.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter, Result as DisplayResult}; 2 | 3 | /// A trait for validating a type. 4 | pub trait Validation where Self: Sized { 5 | /// Validates the type or returns a `ValidationError` if validation fails. 6 | fn validate(&self) -> Result<(), ValidationError>; 7 | /// Validates and returns `Self` or a `ValidationError` if validation fails. 8 | fn validated(self) -> Result { 9 | self.validate().and_then(|_| Ok(self)) 10 | } 11 | } 12 | 13 | /// A validation error. 14 | #[derive(Clone, Debug, PartialEq, Eq)] 15 | pub struct ValidationError { 16 | /// The validation error's description. 17 | pub description: String 18 | } 19 | 20 | impl ValidationError { 21 | /// Creates a `ValidationError` with the given description. 22 | pub fn new(description: &str) -> ValidationError { 23 | ValidationError { 24 | description: description.to_owned() 25 | } 26 | } 27 | } 28 | 29 | impl Display for ValidationError { 30 | fn fmt(&self, f: &mut Formatter) -> DisplayResult { 31 | write!(f, "{}", self.description) 32 | } 33 | } 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use super::super::*; 38 | 39 | #[derive(Clone, Debug, PartialEq, Eq)] 40 | struct Test { 41 | pub value: String 42 | } 43 | 44 | impl Validation for Test { 45 | fn validate(&self) -> Result<(), ValidationError> { 46 | if self.value == "invalid" { 47 | return Err(ValidationError::new("invalid value")); 48 | } 49 | Ok(()) 50 | } 51 | } 52 | 53 | #[test] 54 | fn test_validation() { 55 | let valid_test = Test { value: "valid".to_owned() }; 56 | assert_eq!(valid_test.clone().validate(), Ok(())); 57 | 58 | let valid_test = Test { value: "valid".to_owned() }; 59 | assert_eq!(valid_test.clone().validated(), Ok(valid_test)); 60 | 61 | let invalid_test = Test { value: "invalid".to_owned() }; 62 | assert_eq!(invalid_test.clone().validate(), Err(ValidationError::new("invalid value"))); 63 | 64 | assert_eq!(format!("{}", ValidationError::new("invalid value")), "invalid value".to_owned()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /exar-core/tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate exar; 2 | extern crate rand; 3 | 4 | #[cfg(test)] 5 | extern crate exar_testkit; 6 | 7 | use exar::*; 8 | use exar_testkit::*; 9 | 10 | #[test] 11 | fn integration_test() { 12 | let mut db = Database::new(DatabaseConfig::default()); 13 | 14 | let collection_name = &random_collection_name(); 15 | let connection = db.connect(collection_name).expect("Unable to connect"); 16 | 17 | let test_event = Event::new("data", vec!["tag1", "tag2"]); 18 | assert!(connection.publish(test_event.clone()).is_ok()); 19 | 20 | let query = Query::current(); 21 | let retrieved_events: Vec<_> = connection.subscribe(query).unwrap().take(1).collect(); 22 | let expected_event = test_event.clone().with_id(1).with_timestamp(retrieved_events[0].timestamp); 23 | assert_eq!(retrieved_events, vec![expected_event]); 24 | 25 | assert!(db.drop_collection(collection_name).is_ok()); 26 | assert!(!db.contains_collection(collection_name)); 27 | } 28 | -------------------------------------------------------------------------------- /exar-db/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar-db" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Exar DB's event store with streaming support" 6 | keywords = ["exar", "exar-db", "event", "store", "database"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-db" 8 | documentation = "https://bfil.github.io/exar-db/exar_db/index.html" 9 | license = "AGPL-3.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | clap = "2.1" 14 | exar = { version = "0.1", path = "../exar-core", features = ["rustc-serialization"] } 15 | exar-server = { version = "0.1", path = "../exar-server", features = ["rustc-serialization"] } 16 | log = "0.3" 17 | log4rs = { version = "0.6", features = ["toml_format"] } 18 | rustc-serialize = "0.3" 19 | toml-config = "0.4" 20 | -------------------------------------------------------------------------------- /exar-db/README.md: -------------------------------------------------------------------------------- 1 | # Exar DB 2 | 3 | An event store with streaming support, it uses flat-file based collections. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/exar-server.svg)](https://crates.io/crates/exar-db) 6 | 7 | [Documentation](https://bfil.github.io/exar-db/exar_db/index.html) 8 | 9 | ## Installation 10 | 11 | Install [`Cargo`](https://crates.io/install), then run: 12 | 13 | ``` 14 | cargo install exar-db 15 | ``` 16 | 17 | ## Starting the database 18 | 19 | Simply run `exar-db`. 20 | 21 | ## Configuring the database 22 | 23 | The database can be configured using a `TOML` configuration file, example below: 24 | 25 | ```toml 26 | log4rs_path = "/path/to/log4rs.toml" 27 | [database] 28 | logs_path = "~/exar-db/data" 29 | scanners = { nr_of_scanners = 2, sleep_time_in_ms = 10 } 30 | [database.collections.my-collection] 31 | routing_strategy = "Random" 32 | scanners = { nr_of_scanners = 4, sleep_time_in_ms = 5 } 33 | [server] 34 | host = "127.0.0.1" 35 | port = 38580 36 | username = "my-username" 37 | password = "my-secret" 38 | ``` 39 | 40 | Then run Exar DB by specifying the config file location: `exar-db --config=/path/to/config.toml`. 41 | 42 | For more information about the `database` and `server` configuration sections, 43 | check the documentation about 44 | [DatabaseConfig](https://bfil.github.io/exar-db/exar/struct.DatabaseConfig.html) and 45 | [ServerConfig](https://bfil.github.io/exar-db/exar_server/struct.ServerConfig.html). 46 | 47 | ## Logging 48 | 49 | Logging can be configured using a [log4rs](https://github.com/sfackler/log4rs) config file in `TOML` format, example below: 50 | 51 | ```toml 52 | [appenders.console] 53 | kind = "console" 54 | 55 | [appenders.console.encoder] 56 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 57 | 58 | [appenders.file] 59 | kind = "file" 60 | path = "exar-db.log" 61 | 62 | [appenders.file.encoder] 63 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 64 | 65 | [root] 66 | level = "info" 67 | appenders = ["console", "file"] 68 | ``` 69 | -------------------------------------------------------------------------------- /exar-db/log4rs.toml: -------------------------------------------------------------------------------- 1 | [appenders.console] 2 | kind = "console" 3 | 4 | [appenders.console.encoder] 5 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 6 | 7 | [appenders.file] 8 | kind = "file" 9 | path = "exar-db.log" 10 | 11 | [appenders.file.encoder] 12 | pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 13 | 14 | [root] 15 | level = "info" 16 | appenders = ["console", "file"] 17 | -------------------------------------------------------------------------------- /exar-db/src/config.rs: -------------------------------------------------------------------------------- 1 | use exar::*; 2 | use exar_server::*; 3 | 4 | #[derive(RustcEncodable, RustcDecodable)] 5 | #[derive(Clone, Debug, PartialEq, Eq)] 6 | pub struct Config { 7 | pub log4rs_path: String, 8 | pub database: DatabaseConfig, 9 | pub server: ServerConfig 10 | } 11 | 12 | impl Default for Config { 13 | fn default() -> Config { 14 | Config { 15 | log4rs_path: "log4rs.toml".to_owned(), 16 | database: DatabaseConfig::default(), 17 | server: ServerConfig::default() 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /exar-db/src/main.rs: -------------------------------------------------------------------------------- 1 | //! # Exar DB 2 | //! Exar DB is an event store with streaming support 3 | //! which uses a flat-file for each collection of events 4 | //! 5 | //! ## Installation 6 | //! 7 | //! Install [`Cargo`](https://crates.io/install), then run: 8 | //! 9 | //! ``` 10 | //! cargo install exar-db 11 | //! ``` 12 | //! 13 | //! ## Starting the database 14 | //! 15 | //! Simply run `exar-db`. 16 | //! 17 | //! ## Configuring the database 18 | //! 19 | //! The database can be configured using a `TOML` configuration file, example below: 20 | //! 21 | //! ```toml 22 | //! log4rs_path = "/path/to/log4rs.toml" 23 | //! [database] 24 | //! logs_path = "~/exar-db/data" 25 | //! scanners = { nr_of_scanners = 2, sleep_time_in_ms = 10 } 26 | //! [database.collections.my-collection] 27 | //! routing_strategy = "Random" 28 | //! scanners = { nr_of_scanners = 4, sleep_time_in_ms = 5 } 29 | //! [server] 30 | //! host = "127.0.0.1" 31 | //! port = 38580 32 | //! username = "my-username" 33 | //! password = "my-secret" 34 | //! ``` 35 | //! 36 | //! Then run Exar DB by specifying the config file location: `exar-db --config=/path/to/config.toml`. 37 | //! 38 | //! For more information about the `database` and `server` configuration sections, 39 | //! check the documentation about 40 | //! [DatabaseConfig](https://bfil.github.io/exar-db/exar/struct.DatabaseConfig.html) and 41 | //! [ServerConfig](https://bfil.github.io/exar-db/exar_server/struct.ServerConfig.html). 42 | //! 43 | //! ## Logging 44 | //! 45 | //! Logging can be configured using a [log4rs](https://github.com/sfackler/log4rs) config file in `TOML` format, example below: 46 | //! 47 | //! ```toml 48 | //! [appenders.console] 49 | //! kind = "console" 50 | //! 51 | //! [appenders.console.encoder] 52 | //! pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 53 | //! 54 | //! [appenders.file] 55 | //! kind = "file" 56 | //! path = "exar-db.log" 57 | //! 58 | //! [appenders.file.encoder] 59 | //! pattern = "[{d(%+)(local)}] [{h({l})}] [{t}] {m}{n}" 60 | //! 61 | //! [root] 62 | //! level = "info" 63 | //! appenders = ["console", "file"] 64 | //! ``` 65 | 66 | extern crate clap; 67 | extern crate exar; 68 | extern crate exar_server; 69 | extern crate rustc_serialize; 70 | extern crate toml_config; 71 | 72 | #[macro_use] 73 | extern crate log; 74 | extern crate log4rs; 75 | 76 | mod config; 77 | use config::*; 78 | 79 | use clap::App; 80 | use exar::*; 81 | use exar_server::*; 82 | use log::LogLevelFilter; 83 | use log4rs::append::console::ConsoleAppender; 84 | use log4rs::config::{Appender, Config as Log4rsConfig, Root}; 85 | use std::path::Path; 86 | use toml_config::ConfigFactory; 87 | 88 | fn main() { 89 | let matches = App::new("exar-db") 90 | .version("0.1.0") 91 | .author("Bruno Filippone ") 92 | .about("An event store with streaming support which uses a flat-file for each collection of events") 93 | .args_from_usage( 94 | "-c, --config=[FILE] 'Sets a custom config file'") 95 | .get_matches(); 96 | 97 | let config = match matches.value_of("config") { 98 | Some(config_file) => ConfigFactory::load(Path::new(config_file)), 99 | None => Config::default() 100 | }; 101 | 102 | match log4rs::init_file(config.log4rs_path.clone(), Default::default()) { 103 | Ok(_) => info!("Loaded log4rs config file: {}", config.log4rs_path), 104 | Err(_) => { 105 | let console_appender = Appender::builder() 106 | .build("console".to_owned(), Box::new(ConsoleAppender::builder().build())); 107 | let root = Root::builder() 108 | .appender("console".to_owned()) 109 | .build(LogLevelFilter::Info); 110 | let log4rs_config = Log4rsConfig::builder() 111 | .appender(console_appender) 112 | .build(root).expect("Unable to build log4rs config"); 113 | log4rs::init_config(log4rs_config).expect("Unable to initialize log4rs config"); 114 | info!("Unable to load config file '{}', using default console appender", config.log4rs_path); 115 | } 116 | }; 117 | 118 | let db = Database::new(config.database); 119 | match Server::new(config.server.clone(), db) { 120 | Ok(server) => { 121 | info!("ExarDB running at {}", config.server.address()); 122 | server.listen(); 123 | info!("ExarDB shutting down"); 124 | }, 125 | Err(err) => error!("Unable to run ExarDB: {}", err) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /exar-net/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar-net" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Exar DB's TCP protocol" 6 | keywords = ["exar", "exar-db", "tcp", "protocol"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-net" 8 | documentation = "https://bfil.github.io/exar-db/exar_net/index.html" 9 | license = "AGPL-3.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | exar = { version = "0.1", path = "../exar-core" } 14 | 15 | [dev-dependencies] 16 | exar-testkit = { version = "0.1", path = "../exar-testkit" } 17 | -------------------------------------------------------------------------------- /exar-net/README.md: -------------------------------------------------------------------------------- 1 | # Exar DB's TCP protocol 2 | 3 | Defines the TCP protocol used by Exar DB. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/exar-net.svg)](https://crates.io/crates/exar-net) 6 | 7 | [Documentation](https://bfil.github.io/exar-db/exar_net/index.html) 8 | -------------------------------------------------------------------------------- /exar-net/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Exar DB's TCP protocol 2 | //! This module defines the TCP protocol used by Exar DB. 3 | //! 4 | //! ## Protocol messages 5 | //! The protocol is text-based and uses line-separated messages, 6 | //! each message consists of tab-separated values. 7 | //! 8 | //! ### Connect 9 | //! Message used to initialize a connection to Exar DB. 10 | //! 11 | //! ```text 12 | //! Connect collection [username] [password] 13 | //! ``` 14 | //! 15 | //! - The 1st field is the string `Connect`. 16 | //! - The 2nd field is the collection name. 17 | //! - The 3rd field is the authentication username (optional). 18 | //! - The 4th field is the authentication password (optional). 19 | //! 20 | //! ### Connected 21 | //! Message used to acknowledge a successful connection. 22 | //! 23 | //! ```text 24 | //! Connected 25 | //! ``` 26 | //! 27 | //! - A single field containing the string `Connected`. 28 | //! 29 | //! ### Publish 30 | //! Message used to publish an event into a collection. 31 | //! 32 | //! *It can be used only after a successful connection has been established*. 33 | //! 34 | //! ```text 35 | //! Publish tag1 tag2 timestamp event_data 36 | //! ``` 37 | //! 38 | //! - The 1st field is the string `Publish`. 39 | //! - The 2nd field is a space-separated list of tags, the event must contain at least one tag. 40 | //! - The 3rd field is the event timestamp (in ms), if set to 0 the timestamp will be set by the event logger. 41 | //! - The 4th field is the event data/payload, it can contain tabs (`\t`) but new-lines (`\n`) must be escaped. 42 | //! 43 | //! ### Published 44 | //! Message used to acknowledge a successfully published event. 45 | //! 46 | //! ```text 47 | //! Published event_id 48 | //! ``` 49 | //! 50 | //! - The 1st field is the string `Published`. 51 | //! - The 2nd field is the `id` (or sequence number) of the event that has been published. 52 | //! 53 | //! ### Subscribe 54 | //! Message used to subscribe to an event stream. 55 | //! 56 | //! *It can be used only after a successful connection has been established*. 57 | //! 58 | //! ```text 59 | //! Subscribe live offset limit [tag1] 60 | //! ``` 61 | //! 62 | //! - The 1st field is the string `Subscribe`. 63 | //! - The 2nd field is a boolean specifying wether to keep the subscription listening to real-time events. 64 | //! - The 3rd field is the query offset. 65 | //! - The 4th field is the maximum number of events to consume, if set to 0 a limit is not set. 66 | //! - The 5th field is the tag the events must contain (optional). 67 | //! 68 | //! ### Subscribed 69 | //! Message used to acknowledge a successful subscription. 70 | //! 71 | //! ```text 72 | //! Subscribed 73 | //! ``` 74 | //! 75 | //! - A single field containing the string `Subscribed`. 76 | //! 77 | //! ### Event 78 | //! Message containing an event. 79 | //! 80 | //! *It is received after a successful subscription*. 81 | //! 82 | //! ```text 83 | //! Event event_id tag1 tag2 timestamp event_data 84 | //! ``` 85 | //! 86 | //! - The 1st field is the string `Event`. 87 | //! - The 2nd field is the `id` (or sequence number) of the event. 88 | //! - The 3rd field is a space-separated list of event tags. 89 | //! - The 4th field is the event timestamp (in ms). 90 | //! - The 5th field is the event data/payload. 91 | //! 92 | //! ### EndOfEventStream 93 | //! Message signaling the end of an event stream. 94 | //! 95 | //! *It is received after a `Subscribed` or a list of `Event`s*. 96 | //! 97 | //! ```text 98 | //! EndOfEventStream 99 | //! ``` 100 | //! 101 | //! - A single field containing the string `EndOfEventStream`. 102 | //! 103 | //! ### Error 104 | //! Message containing an error. 105 | //! 106 | //! *It can be received after a `Connect`, `Publish`, `Subscribe`, or during an event stream*. 107 | //! 108 | //! ```text 109 | //! Error type [subtype] description 110 | //! ``` 111 | //! 112 | //! - The 1st field is the string `Error`. 113 | //! - The 2nd field is the type of the error, possible values are: 114 | //! `AuthenticationError`, `ConnectionError`, `EventStreamError`, `IoError`, `ParseError`, 115 | //! `SubscriptionError`, `ValidationError`. 116 | //! - The 3rd field is the sub-type of the error (optional), possible values are: 117 | //! `Empty` or `Closed` if the error type is `EventStreamError`, 118 | //! `ParseError` or `MissingField` if the error type is `ParseError`, 119 | //! or a stringified value of `std::io::ErrorKind` if the error type is `IoError`. 120 | //! - The 4th field is the error message/description. 121 | //! 122 | 123 | #[macro_use] 124 | extern crate exar; 125 | 126 | #[cfg(test)] #[macro_use] 127 | extern crate exar_testkit; 128 | 129 | mod message; 130 | mod stream; 131 | 132 | pub use self::message::*; 133 | pub use self::stream::*; 134 | -------------------------------------------------------------------------------- /exar-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar-server" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Exar DB's TCP server" 6 | keywords = ["exar", "exar-db", "tcp", "server"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-server" 8 | documentation = "https://bfil.github.io/exar-db/exar_server/index.html" 9 | license = "AGPL-3.0" 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | exar = { version = "0.1", path = "../exar-core" } 14 | exar-net = { version = "0.1", path = "../exar-net" } 15 | log = "0.3" 16 | rustc-serialize = { optional = true, version = "0.3" } 17 | serde = { optional = true, version = "0.9" } 18 | serde_derive = { optional = true, version = "0.9" } 19 | 20 | [features] 21 | rustc-serialization = ["rustc-serialize"] 22 | serde-serialization = ["serde", "serde_derive"] 23 | 24 | [dev-dependencies] 25 | exar-testkit = { version = "0.1", path = "../exar-testkit" } 26 | -------------------------------------------------------------------------------- /exar-server/README.md: -------------------------------------------------------------------------------- 1 | # Exar DB's Server 2 | 3 | A server implementation that uses Exar DB's TCP protocol. 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/exar-server.svg)](https://crates.io/crates/exar-server) 6 | 7 | [Documentation](https://bfil.github.io/exar-db/exar_server/index.html) 8 | -------------------------------------------------------------------------------- /exar-server/src/config.rs: -------------------------------------------------------------------------------- 1 | /// Exar DB's server configuration. 2 | /// 3 | /// # Examples 4 | /// ``` 5 | /// extern crate exar_server; 6 | /// 7 | /// # fn main() { 8 | /// use exar_server::*; 9 | /// 10 | /// let config = ServerConfig { 11 | /// host: "127.0.0.1".to_owned(), 12 | /// port: 38580, 13 | /// username: Some("username".to_owned()), 14 | /// password: Some("password".to_owned()) 15 | /// }; 16 | /// # } 17 | /// ``` 18 | #[cfg_attr(feature = "rustc-serialization", derive(RustcEncodable, RustcDecodable))] 19 | #[cfg_attr(feature = "serde-serialization", derive(Serialize, Deserialize))] 20 | #[derive(Clone, Debug, PartialEq, Eq)] 21 | pub struct ServerConfig { 22 | /// The server host. 23 | pub host: String, 24 | /// The server port. 25 | pub port: u16, 26 | /// The server authentication's username. 27 | pub username: Option, 28 | /// The server authentication's password. 29 | pub password: Option 30 | } 31 | 32 | impl Default for ServerConfig { 33 | fn default() -> ServerConfig { 34 | ServerConfig { 35 | host: "127.0.0.1".to_owned(), 36 | port: 38580, 37 | username: None, 38 | password: None 39 | } 40 | } 41 | } 42 | 43 | impl ServerConfig { 44 | /// Returns a string representation of the server address (`host:port`). 45 | pub fn address(&self) -> String { 46 | format!("{}:{}", self.host, self.port) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /exar-server/src/credentials.rs: -------------------------------------------------------------------------------- 1 | /// A structure containing credentials. 2 | #[derive(Clone, Debug, PartialEq, Eq)] 3 | pub struct Credentials { 4 | /// The username. 5 | pub username: Option, 6 | /// The password. 7 | pub password: Option 8 | } 9 | 10 | impl Credentials { 11 | /// Creates new `Credentials`. 12 | pub fn new(username: &str, password: &str) -> Credentials { 13 | Credentials { 14 | username: Some(username.to_owned()), 15 | password: Some(password.to_owned()) 16 | } 17 | } 18 | 19 | /// Returns empty `Credentials`. 20 | pub fn empty() -> Credentials { 21 | Credentials { 22 | username: None, 23 | password: None 24 | } 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_constructors() { 34 | let credentials = Credentials::new("username", "password"); 35 | assert_eq!(credentials.username, Some("username".to_owned())); 36 | assert_eq!(credentials.password, Some("password".to_owned())); 37 | 38 | let credentials = Credentials::empty(); 39 | assert_eq!(credentials.username, None); 40 | assert_eq!(credentials.password, None); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /exar-server/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Exar DB's server 2 | //! This module contains a server implementation that uses Exar DB's TCP protocol. 3 | //! 4 | //! It uses the one thread per connection model. 5 | //! 6 | //! ## Server Initialization 7 | //! ```no_run 8 | //! extern crate exar; 9 | //! extern crate exar_server; 10 | //! 11 | //! # fn main() { 12 | //! use exar::*; 13 | //! use exar_server::*; 14 | //! 15 | //! let db = Database::new(DatabaseConfig::default()); 16 | //! 17 | //! let server_config = ServerConfig::default(); 18 | //! let server = Server::new(server_config.clone(), db).unwrap(); 19 | //! 20 | //! println!("ExarDB's server running at {}", server_config.address()); 21 | //! server.listen(); 22 | //! println!("ExarDB's server shutting down"); 23 | //! # } 24 | //! ``` 25 | 26 | extern crate exar; 27 | extern crate exar_net; 28 | 29 | #[cfg(feature = "rustc-serialization")] extern crate rustc_serialize; 30 | #[cfg(feature = "serde-serialization")] extern crate serde; 31 | #[cfg(feature = "serde-serialization")] #[macro_use] extern crate serde_derive; 32 | 33 | #[cfg(test)] 34 | extern crate exar_testkit; 35 | 36 | #[macro_use] 37 | extern crate log; 38 | 39 | mod credentials; 40 | mod config; 41 | mod handler; 42 | mod server; 43 | 44 | pub use self::credentials::*; 45 | pub use self::config::*; 46 | pub use self::handler::*; 47 | pub use self::server::*; 48 | -------------------------------------------------------------------------------- /exar-testkit/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exar-testkit" 3 | version = "0.1.0" 4 | authors = ["Bruno Filippone "] 5 | description = "Offers some utility methods to facilitate testing Exar DB's modules" 6 | keywords = ["exar", "exar-db", "testkit"] 7 | repository = "https://github.com/bfil/exar-db/tree/master/exar-testkit" 8 | license = "AGPL-3.0" 9 | 10 | [dependencies] 11 | rand = "0.3" 12 | -------------------------------------------------------------------------------- /exar-testkit/src/collections.rs: -------------------------------------------------------------------------------- 1 | use rand; 2 | use rand::Rng; 3 | 4 | pub fn random_collection_name() -> String { 5 | rand::thread_rng() 6 | .gen_ascii_chars() 7 | .take(10) 8 | .collect::() 9 | } 10 | 11 | pub fn invalid_collection_name() -> String { 12 | "missing-directory/error".to_owned() 13 | } 14 | -------------------------------------------------------------------------------- /exar-testkit/src/encoding.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! assert_encoded_eq { 3 | ($left:expr, $right:expr) => ( assert_eq!($left.to_tab_separated_string(), $right) ) 4 | } 5 | 6 | #[macro_export] 7 | macro_rules! assert_decoded_eq { 8 | ($left:expr, $right:expr) => ( 9 | assert_eq!(FromTabSeparatedStr::from_tab_separated_str($left), Ok($right)) 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /exar-testkit/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(const_fn)] 2 | 3 | extern crate rand; 4 | 5 | mod collections; 6 | mod encoding; 7 | mod net; 8 | 9 | pub use self::collections::*; 10 | pub use self::encoding::*; 11 | pub use self::net::*; 12 | -------------------------------------------------------------------------------- /exar-testkit/src/net.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; 3 | use std::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | static PORT: AtomicUsize = AtomicUsize::new(0); 6 | 7 | fn base_port() -> u16 { 8 | let cwd = env::current_dir().unwrap(); 9 | let dirs = ["32-opt", "32-nopt", "musl-64-opt", "cross-opt", 10 | "64-opt", "64-nopt", "64-opt-vg", "64-debug-opt", 11 | "all-opt", "snap3", "dist"]; 12 | dirs.iter().enumerate().find(|&(_, dir)| { 13 | cwd.to_str().unwrap().contains(dir) 14 | }).map(|p| p.0).unwrap_or(0) as u16 * 1000 + 19600 15 | } 16 | 17 | fn next_test_ip4() -> SocketAddr { 18 | let port = PORT.fetch_add(1, Ordering::SeqCst) as u16 + base_port(); 19 | SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) 20 | } 21 | 22 | pub fn with_addr(f: &mut FnMut(SocketAddr)) { 23 | f(next_test_ip4()); 24 | } 25 | -------------------------------------------------------------------------------- /exar-ui/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | jspm_packages 3 | bower_components 4 | .idea 5 | .DS_STORE 6 | dist 7 | build/reports 8 | coverage 9 | test/e2e/dist 10 | Exar UI-* 11 | -------------------------------------------------------------------------------- /exar-ui/.npmignore: -------------------------------------------------------------------------------- 1 | jspm_packages 2 | bower_components 3 | .idea -------------------------------------------------------------------------------- /exar-ui/README.md: -------------------------------------------------------------------------------- 1 | # Exar UI 2 | 3 | A basic Exar DB's user interface, built with [Aurelia](http://aurelia.io), [Electron](http://electron.atom.io) and [TypeScript](https://www.typescriptlang.org) 4 | 5 | ## Running The App 6 | 7 | To run the app, follow these steps. 8 | 9 | 1. Ensure that [NodeJS](http://nodejs.org/) is installed. This provides the platform on which the build tooling runs. 10 | 2. From the project folder, execute the following command: 11 | ```shell 12 | npm install 13 | ``` 14 | 3. Ensure that [Gulp](http://gulpjs.com/) is installed globally. If you need to install it, use the following command: 15 | ```shell 16 | npm install -g gulp 17 | ``` 18 | > **Note:** Gulp must be installed globally, but a local version will also be installed to ensure a compatible version is used for the project. 19 | 4. Ensure that [jspm](http://jspm.io/) is installed globally. If you need to install it, use the following command: 20 | ```shell 21 | npm install -g jspm 22 | ``` 23 | > **Note:** jspm must be installed globally, but a local version will also be installed to ensure a compatible version is used for the project. 24 | 25 | > **Note:** Sometimes jspm queries GitHub to install packages, but GitHub has a rate limit on anonymous API requests. If you receive a rate limit error, you need to configure jspm with your GitHub credentials. You can do this by executing `jspm registry config github` and following the prompts. If you choose to authorize jspm by an access token instead of giving your password (see GitHub `Settings > Personal Access Tokens`), `public_repo` access for the token is required. 26 | 5. Install the client-side dependencies with jspm: 27 | 28 | ```shell 29 | jspm install -y 30 | ``` 31 | >**Note:** Windows users, if you experience an error of "unknown command unzip" you can solve this problem by doing `npm install -g unzip` and then re-running `jspm install`. 32 | 33 | 6. Build the project: 34 | 35 | ```shell 36 | gulp build 37 | ``` 38 | 39 | 7. Install [Electron](http://electron.atom.io) 40 | 41 | ```shell 42 | npm install electron-prebuilt -g 43 | ``` 44 | 8. To start the app, execute the following command: 45 | 46 | ```shell 47 | electron index.js 48 | ``` 49 | >**Note:** If you use electron every time or are packaging and so-forth, Then change this line in package.json from 50 | `"main": "dist/main.js",` to `"main": "index.js",` 51 | Build the app (this will give you a dist directory) 52 | ```shell 53 | gulp build 54 | ``` 55 | To start the app, execute the following command: 56 | ```shell 57 | electron . 58 | ``` 59 | 60 | ## Development 61 | 62 | 1. Run the app in watch mode, execute the following command: 63 | 64 | ```shell 65 | gulp watch 66 | ``` 67 | 68 | 2. Then start the app in electron in a separate terminal, execute the following command: 69 | 70 | ```shell 71 | electron index.js 72 | ``` 73 | 74 | ## Running The Unit Tests 75 | 76 | To run the unit tests, first ensure that you have followed the steps above in order to install all dependencies and successfully build the library. Once you have done that, proceed with these additional steps: 77 | 78 | 1. Ensure that the [Karma](http://karma-runner.github.io/) CLI is installed. If you need to install it, use the following command: 79 | 80 | ```shell 81 | npm install -g karma-cli 82 | ``` 83 | 2. Install Aurelia libs for test visibility: 84 | 85 | ```shell 86 | jspm install aurelia-framework 87 | jspm install aurelia-http-client 88 | jspm install aurelia-router 89 | ``` 90 | 3. You can now run the tests with this command: 91 | 92 | ```shell 93 | karma start 94 | ``` 95 | -------------------------------------------------------------------------------- /exar-ui/aurelia.protractor.js: -------------------------------------------------------------------------------- 1 | /* Aurelia Protractor Plugin */ 2 | function addValueBindLocator() { 3 | by.addLocator('valueBind', function (bindingModel, opt_parentElement) { 4 | var using = opt_parentElement || document; 5 | var matches = using.querySelectorAll('*[value\\.bind="' + bindingModel +'"]'); 6 | var result; 7 | 8 | if (matches.length === 0) { 9 | result = null; 10 | } else if (matches.length === 1) { 11 | result = matches[0]; 12 | } else { 13 | result = matches; 14 | } 15 | 16 | return result; 17 | }); 18 | } 19 | 20 | function loadAndWaitForAureliaPage(pageUrl) { 21 | browser.get(pageUrl); 22 | return browser.executeAsyncScript( 23 | 'var cb = arguments[arguments.length - 1];' + 24 | 'document.addEventListener("aurelia-composed", function (e) {' + 25 | ' cb("Aurelia App composed")' + 26 | '}, false);' 27 | ).then(function(result){ 28 | console.log(result); 29 | return result; 30 | }); 31 | } 32 | 33 | function waitForRouterComplete() { 34 | return browser.executeAsyncScript( 35 | 'var cb = arguments[arguments.length - 1];' + 36 | 'document.querySelector("[aurelia-app]")' + 37 | '.aurelia.subscribeOnce("router:navigation:complete", function() {' + 38 | ' cb(true)' + 39 | '});' 40 | ).then(function(result){ 41 | return result; 42 | }); 43 | } 44 | 45 | /* Plugin hooks */ 46 | exports.setup = function(config) { 47 | // Ignore the default Angular synchronization helpers 48 | browser.ignoreSynchronization = true; 49 | 50 | // add the aurelia specific valueBind locator 51 | addValueBindLocator(); 52 | 53 | // attach a new way to browser.get a page and wait for Aurelia to complete loading 54 | browser.loadAndWaitForAureliaPage = loadAndWaitForAureliaPage; 55 | 56 | // wait for router navigations to complete 57 | browser.waitForRouterComplete = waitForRouterComplete; 58 | }; 59 | 60 | exports.teardown = function(config) {}; 61 | exports.postResults = function(config) {}; 62 | -------------------------------------------------------------------------------- /exar-ui/build.sh: -------------------------------------------------------------------------------- 1 | electron-packager . "Exar UI" --platform=darwin --arch=x64 --version=0.36.11 --overwrite --ignore="node_modules/*" -------------------------------------------------------------------------------- /exar-ui/build/args.js: -------------------------------------------------------------------------------- 1 | var yargs = require('yargs'); 2 | 3 | var argv = yargs.argv; 4 | var validBumpTypes = 'major|minor|patch|prerelease'.split('|'); 5 | var bump = (argv.bump || 'patch').toLowerCase(); 6 | 7 | if (validBumpTypes.indexOf(bump) === -1) { 8 | throw new Error('Unrecognized bump "' + bump + '".'); 9 | } 10 | 11 | module.exports = { 12 | bump: bump, 13 | depth: parseInt(argv.depth || '0') 14 | }; 15 | -------------------------------------------------------------------------------- /exar-ui/build/bundles.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "bundles": { 3 | "dist/app-build": { 4 | "includes": [ 5 | "[**/*.js]", 6 | "**/*.html!text", 7 | "**/*.css!text" 8 | ], 9 | "options": { 10 | "inject": true, 11 | "minify": true, 12 | "depCache": true, 13 | "rev": false 14 | } 15 | }, 16 | "dist/aurelia": { 17 | "includes": [ 18 | "aurelia-framework", 19 | "aurelia-bootstrapper", 20 | "aurelia-fetch-client", 21 | "aurelia-router", 22 | "aurelia-animator-css", 23 | "aurelia-templating-binding", 24 | "aurelia-polyfills", 25 | "aurelia-templating-resources", 26 | "aurelia-templating-router", 27 | "aurelia-loader-default", 28 | "aurelia-history-browser", 29 | "aurelia-logging-console", 30 | "bootstrap", 31 | "bootstrap/css/bootstrap.css!text", 32 | "fetch", 33 | "jquery" 34 | ], 35 | "options": { 36 | "inject": true, 37 | "minify": true, 38 | "depCache": false, 39 | "rev": false 40 | } 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /exar-ui/build/export.js: -------------------------------------------------------------------------------- 1 | // this file provides a list of unbundled files that 2 | // need to be included when exporting the application 3 | // for production. 4 | module.exports = { 5 | 'list': [ 6 | 'index.html', 7 | 'config.js', 8 | 'favicon.ico', 9 | 'LICENSE', 10 | 'jspm_packages/system.js', 11 | 'jspm_packages/system-polyfills.js', 12 | 'jspm_packages/system-csp-production.js', 13 | 'styles/styles.css' 14 | ], 15 | // this section lists any jspm packages that have 16 | // unbundled resources that need to be exported. 17 | // these files are in versioned folders and thus 18 | // must be 'normalized' by jspm to get the proper 19 | // path. 20 | 'normalize': [ 21 | [ 22 | // include font-awesome.css and its fonts files 23 | 'font-awesome', [ 24 | '/css/font-awesome.min.css', 25 | '/fonts/*' 26 | ] 27 | ], [ 28 | // include bootstrap's font files 29 | 'bootstrap', [ 30 | '/fonts/*' 31 | ] 32 | ] 33 | ] 34 | }; 35 | -------------------------------------------------------------------------------- /exar-ui/build/paths.js: -------------------------------------------------------------------------------- 1 | var appRoot = 'src/'; 2 | var outputRoot = 'dist/'; 3 | var exporSrvtRoot = 'export/' 4 | 5 | module.exports = { 6 | root: appRoot, 7 | source: appRoot + '**/*.ts', 8 | html: appRoot + '**/*.html', 9 | css: appRoot + '**/*.css', 10 | style: 'styles/**/*.css', 11 | output: outputRoot, 12 | exportSrv: exporSrvtRoot, 13 | doc: './doc', 14 | e2eSpecsSrc: 'test/e2e/src/**/*.ts', 15 | e2eSpecsDist: 'test/e2e/dist/', 16 | dtsSrc: [ 17 | './typings/**/*.d.ts', 18 | './custom_typings/**/*.d.ts' 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var changed = require('gulp-changed'); 4 | var plumber = require('gulp-plumber'); 5 | var sourcemaps = require('gulp-sourcemaps'); 6 | var paths = require('../paths'); 7 | var assign = Object.assign || require('object.assign'); 8 | var notify = require('gulp-notify'); 9 | var browserSync = require('browser-sync'); 10 | var typescript = require('gulp-typescript'); 11 | 12 | // transpiles changed es6 files to SystemJS format 13 | // the plumber() call prevents 'pipe breaking' caused 14 | // by errors from other gulp plugins 15 | // https://www.npmjs.com/package/gulp-plumber 16 | var typescriptCompiler = typescriptCompiler || null; 17 | gulp.task('build-system', function() { 18 | if(!typescriptCompiler) { 19 | typescriptCompiler = typescript.createProject('tsconfig.json', { 20 | "typescript": require('typescript') 21 | }); 22 | } 23 | return gulp.src(paths.dtsSrc.concat(paths.source)) 24 | .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')})) 25 | .pipe(changed(paths.output, {extension: '.ts'})) 26 | .pipe(sourcemaps.init({loadMaps: true})) 27 | .pipe(typescript(typescriptCompiler)) 28 | .pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: '/src'})) 29 | .pipe(gulp.dest(paths.output)); 30 | }); 31 | 32 | // copies changed html files to the output directory 33 | gulp.task('build-html', function() { 34 | return gulp.src(paths.html) 35 | .pipe(changed(paths.output, {extension: '.html'})) 36 | .pipe(gulp.dest(paths.output)); 37 | }); 38 | 39 | // copies changed css files to the output directory 40 | gulp.task('build-css', function() { 41 | return gulp.src(paths.css) 42 | .pipe(changed(paths.output, {extension: '.css'})) 43 | .pipe(gulp.dest(paths.output)) 44 | .pipe(browserSync.stream()); 45 | }); 46 | 47 | // this task calls the clean task (located 48 | // in ./clean.js), then runs the build-system 49 | // and build-html tasks in parallel 50 | // https://www.npmjs.com/package/gulp-run-sequence 51 | gulp.task('build', function(callback) { 52 | return runSequence( 53 | 'clean', 54 | ['build-system', 'build-html', 'build-css'], 55 | callback 56 | ); 57 | }); 58 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/bundle.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var bundler = require('aurelia-bundler'); 3 | var bundles = require('../bundles.js'); 4 | 5 | var config = { 6 | force: true, 7 | baseURL: '.', 8 | configPath: './config.js', 9 | bundles: bundles.bundles 10 | }; 11 | 12 | gulp.task('bundle', ['build'], function() { 13 | return bundler.bundle(config); 14 | }); 15 | 16 | gulp.task('unbundle', function() { 17 | return bundler.unbundle(config); 18 | }); 19 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var del = require('del'); 4 | var vinylPaths = require('vinyl-paths'); 5 | 6 | // deletes all files in the output path 7 | gulp.task('clean', ['unbundle'], function() { 8 | return gulp.src([paths.output]) 9 | .pipe(vinylPaths(del)); 10 | }); 11 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/dev.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var tools = require('aurelia-tools'); 3 | var args = require('../args'); 4 | 5 | // source code for the tasks called in this file 6 | // is located at: https://github.com/aurelia/tools/blob/master/src/dev.js 7 | 8 | // updates dependencies in this folder 9 | // from folders in the parent directory 10 | gulp.task('update-own-deps', function() { 11 | tools.updateOwnDependenciesFromLocalRepositories(args.depth); 12 | }); 13 | 14 | // quickly pulls in all of the aurelia 15 | // github repos, placing them up one directory 16 | // from where the command is executed, 17 | // then runs `npm install` 18 | // and `gulp build` for each repo 19 | gulp.task('build-dev-env', function() { 20 | tools.buildDevEnv(); 21 | }); 22 | 23 | // quickly pulls in all of the aurelia 24 | // github repos, placing them up one directory 25 | // from where the command is executed 26 | gulp.task('pull-dev-env', function() { 27 | tools.pullDevEnv(); 28 | }); 29 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/e2e.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var plumber = require('gulp-plumber'); 4 | var webdriverUpdate = require('gulp-protractor').webdriver_update; 5 | var webdriverStandalone = require('gulp-protractor').webdriver_standalone; 6 | var protractor = require('gulp-protractor').protractor; 7 | var typescript = require('gulp-typescript'); 8 | var assign = Object.assign || require('object.assign'); 9 | var del = require('del'); 10 | 11 | // for full documentation of gulp-protractor, 12 | // please check https://github.com/mllrsohn/gulp-protractor 13 | gulp.task('webdriver-update', webdriverUpdate); 14 | gulp.task('webdriver-standalone', ['webdriver-update'], webdriverStandalone); 15 | 16 | gulp.task('clean-e2e', function() { 17 | return del(paths.e2eSpecsDist + '*'); 18 | }); 19 | 20 | // transpiles files in 21 | // /test/e2e/src/ from es6 to es5 22 | // then copies them to test/e2e/dist/ 23 | var typescriptCompiler = typescriptCompiler || null; 24 | gulp.task('build-e2e', ['clean-e2e'], function() { 25 | if(!typescriptCompiler) { 26 | typescriptCompiler = typescript.createProject('tsconfig.json', { 27 | "typescript": require('typescript'), 28 | module: 'commonjs' 29 | }); 30 | } 31 | return gulp.src(paths.dtsSrc.concat(paths.e2eSpecsSrc)) 32 | .pipe(typescript(typescriptCompiler)) 33 | .pipe(gulp.dest(paths.e2eSpecsDist)); 34 | }); 35 | 36 | // runs build-e2e task 37 | // then runs end to end tasks 38 | // using Protractor: http://angular.github.io/protractor/ 39 | gulp.task('e2e', ['build-e2e'], function(cb) { 40 | return gulp.src(paths.e2eSpecsDist + '**/*.js') 41 | .pipe(protractor({ 42 | configFile: 'protractor.conf.js', 43 | args: ['--baseUrl', 'http://127.0.0.1:9000'] 44 | })) 45 | .on('end', function() { process.exit(); }) 46 | .on('error', function(e) { throw e; }); 47 | }); 48 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/export-release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('gulp'); 4 | const runSequence = require('run-sequence'); 5 | const del = require('del'); 6 | const vinylPaths = require('vinyl-paths'); 7 | const jspm = require('jspm'); 8 | const paths = require('../paths'); 9 | const bundles = require('../bundles.js'); 10 | const resources = require('../export.js'); 11 | 12 | function getBundles() { 13 | let bl = []; 14 | for (let b in bundles.bundles) { 15 | bl.push(b + '*.js'); 16 | } 17 | return bl; 18 | } 19 | 20 | function getExportList() { 21 | return resources.list.concat(getBundles()); 22 | } 23 | 24 | function normalizeExportPaths() { 25 | const pathsToNormalize = resources.normalize; 26 | 27 | let promises = pathsToNormalize.map(pathSet => { 28 | const packageName = pathSet[ 0 ]; 29 | const fileList = pathSet[ 1 ]; 30 | 31 | return jspm.normalize(packageName).then((normalized) => { 32 | const packagePath = normalized.substring(normalized.indexOf('jspm_packages'), normalized.lastIndexOf('.js')); 33 | return fileList.map(file => packagePath + file); 34 | }); 35 | }); 36 | 37 | return Promise.all(promises) 38 | .then((normalizedPaths) => { 39 | return normalizedPaths.reduce((prev, curr) => prev.concat(curr), []); 40 | }); 41 | } 42 | 43 | // deletes all files in the output path 44 | gulp.task('clean-export', function() { 45 | return gulp.src([ paths.exportSrv ]) 46 | .pipe(vinylPaths(del)); 47 | }); 48 | 49 | gulp.task('export-copy', function() { 50 | return gulp.src(getExportList(), { base: '.' }) 51 | .pipe(gulp.dest(paths.exportSrv)); 52 | }); 53 | 54 | gulp.task('export-normalized-resources', function() { 55 | return normalizeExportPaths().then(normalizedPaths => { 56 | return gulp.src(normalizedPaths, { base: '.' }) 57 | .pipe(gulp.dest(paths.exportSrv)); 58 | }); 59 | }); 60 | 61 | // use after prepare-release 62 | gulp.task('export', function(callback) { 63 | return runSequence( 64 | 'bundle', 65 | 'clean-export', 66 | 'export-normalized-resources', 67 | 'export-copy', 68 | callback 69 | ); 70 | }); 71 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/lint.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var tslint = require('gulp-tslint'); 4 | 5 | gulp.task('lint', function() { 6 | return gulp.src(paths.source) 7 | .pipe(tslint()) 8 | .pipe(tslint.report('prose', { 9 | emitError: false 10 | })); 11 | }); 12 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/prepare-release.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var paths = require('../paths'); 4 | var changelog = require('conventional-changelog'); 5 | var fs = require('fs'); 6 | var bump = require('gulp-bump'); 7 | var args = require('../args'); 8 | 9 | // utilizes the bump plugin to bump the 10 | // semver for the repo 11 | gulp.task('bump-version', function() { 12 | return gulp.src(['./package.json']) 13 | .pipe(bump({type: args.bump})) //major|minor|patch|prerelease 14 | .pipe(gulp.dest('./')); 15 | }); 16 | 17 | // generates the CHANGELOG.md file based on commit 18 | // from git commit messages 19 | gulp.task('changelog', function(callback) { 20 | var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); 21 | 22 | return changelog({ 23 | repository: pkg.repository.url, 24 | version: pkg.version, 25 | file: paths.doc + '/CHANGELOG.md' 26 | }, function(err, log) { 27 | fs.writeFileSync(paths.doc + '/CHANGELOG.md', log); 28 | }); 29 | }); 30 | 31 | // calls the listed sequence of tasks in order 32 | gulp.task('prepare-release', function(callback) { 33 | return runSequence( 34 | 'build', 35 | 'lint', 36 | 'bump-version', 37 | 'changelog', 38 | callback 39 | ); 40 | }); 41 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/serve.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserSync = require('browser-sync'); 3 | var historyApiFallback = require('connect-history-api-fallback') 4 | 5 | function enableCORS(req, res, next) { 6 | res.setHeader('Access-Control-Allow-Origin', '*'); 7 | res.setHeader('Access-Control-Allow-Methods', 'GET'); 8 | res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Connection, Content-Type, Accept, Accept-Encoding, Accept-Language,' + 9 | ' Host, Referer, User-Agent, Overwrite, Destination, Depth, X-Token, X-File-Size, If-Modified-Since, X-File-Name, Cache-Control'); 10 | if (req.method.match(/^OPTIONS$/i)) return res.end(); 11 | return next(); 12 | } 13 | 14 | // this task utilizes the browsersync plugin 15 | // to create a dev server instance 16 | // at http://localhost:8000 17 | gulp.task('serve', ['build'], function(done) { 18 | browserSync({ 19 | online: false, 20 | open: false, 21 | port: 8000, 22 | server: { 23 | baseDir: ['.'], 24 | middleware: [ historyApiFallback(), enableCORS ] 25 | } 26 | }, done); 27 | }); 28 | 29 | // this task utilizes the browsersync plugin 30 | // to create a dev server instance 31 | // at http://localhost:8000 32 | gulp.task('serve-bundle', ['bundle'], function(done) { 33 | browserSync({ 34 | online: false, 35 | open: false, 36 | port: 8000, 37 | server: { 38 | baseDir: ['.'], 39 | middleware: [ historyApiFallback(), enableCORS ] 40 | } 41 | }, done); 42 | }); 43 | 44 | // this task utilizes the browsersync plugin 45 | // to create a dev server instance 46 | // at http://localhost:8000 47 | gulp.task('serve-export', ['export'], function(done) { 48 | browserSync({ 49 | online: false, 50 | open: false, 51 | port: 8000, 52 | server: { 53 | baseDir: ['./export'], 54 | middleware: [ historyApiFallback(), enableCORS ] 55 | } 56 | }, done); 57 | }); 58 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/test.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var Karma = require('karma').Server; 3 | 4 | /** 5 | * Run test once and exit 6 | */ 7 | gulp.task('test', function (done) { 8 | new Karma({ 9 | configFile: __dirname + '/../../karma.conf.js', 10 | singleRun: true 11 | }, done).start(); 12 | }); 13 | 14 | /** 15 | * Watch for file changes and re-run tests on each change 16 | */ 17 | gulp.task('tdd', function (done) { 18 | new Karma({ 19 | configFile: __dirname + '/../../karma.conf.js' 20 | }, done).start(); 21 | }); 22 | -------------------------------------------------------------------------------- /exar-ui/build/tasks/watch.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var browserSync = require('browser-sync'); 4 | 5 | // outputs changes to files to the console 6 | function reportChange(event) { 7 | console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); 8 | } 9 | 10 | // this task wil watch for changes 11 | // to js, html, and css files and call the 12 | // reportChange method. Also, by depending on the 13 | // serve task, it will instantiate a browserSync session 14 | gulp.task('watch', ['serve'], function() { 15 | gulp.watch(paths.source, ['build-system', browserSync.reload]).on('change', reportChange); 16 | gulp.watch(paths.html, ['build-html', browserSync.reload]).on('change', reportChange); 17 | gulp.watch(paths.css, ['build-css', browserSync.reload]).on('change', reportChange); 18 | gulp.watch(paths.style, browserSync.reload).on('change', reportChange); 19 | }); 20 | -------------------------------------------------------------------------------- /exar-ui/custom_typings/tcp-socket.d.ts: -------------------------------------------------------------------------------- 1 | interface TCPSocketStatic { 2 | open(host: string, port: number): TCPSocket; 3 | } 4 | 5 | interface TCPSocket { 6 | send(data: ArrayBufferView); 7 | close(); 8 | 9 | onopen: any; 10 | ondata: (ArrayBufferView) => any; 11 | onerror: any; 12 | onclose: any; 13 | ondrain: any; 14 | } 15 | 16 | interface Navigator { 17 | TCPSocket: TCPSocketStatic; 18 | } 19 | -------------------------------------------------------------------------------- /exar-ui/custom_typings/text-encoding.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for text-encoding 2 | // Project: https://github.com/inexorabletash/text-encoding 3 | // Definitions by: MIZUNE Pine 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped 5 | 6 | declare module TextEncoding { 7 | interface TextDecoderOptions { 8 | fatal?: boolean; 9 | ignoreBOM?: boolean; 10 | } 11 | 12 | interface TextDecodeOptions { 13 | stream?: boolean; 14 | } 15 | 16 | interface TextEncoderOptions { 17 | NONSTANDARD_allowLegacyEncoding?: boolean; 18 | } 19 | 20 | interface TextDecoder { 21 | encoding: string; 22 | fatal: boolean; 23 | ignoreBOM: boolean; 24 | decode(input?: ArrayBufferView, options?: TextDecodeOptions): string; 25 | } 26 | 27 | interface TextEncoder { 28 | encoding: string; 29 | encode(input?: string, options?: TextEncodeOptions): Uint8Array; 30 | } 31 | 32 | interface TextEncodeOptions { 33 | stream?: boolean; 34 | } 35 | } 36 | 37 | declare var TextDecoder: { 38 | (label?: string, options?: TextEncoding.TextDecoderOptions): TextEncoding.TextDecoder; 39 | new (label?: string, options?: TextEncoding.TextDecoderOptions): TextEncoding.TextDecoder; 40 | }; 41 | 42 | declare var TextEncoder: { 43 | (utfLabel?: string, options?: TextEncoding.TextEncoderOptions): TextEncoding.TextEncoder; 44 | new (utfLabel?: string, options?: TextEncoding.TextEncoderOptions): TextEncoding.TextEncoder; 45 | }; 46 | -------------------------------------------------------------------------------- /exar-ui/gulpfile.js: -------------------------------------------------------------------------------- 1 | // all gulp tasks are located in the ./build/tasks directory 2 | // gulp configuration is in files in ./build directory 3 | require('require-dir')('build/tasks'); 4 | -------------------------------------------------------------------------------- /exar-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exar UI 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
Loading Exar UI..
13 | 14 |
15 | 16 | 17 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /exar-ui/index.js: -------------------------------------------------------------------------------- 1 | //Note: This file is provided as an aid to help you get up and running with 2 | //Electron for desktop apps. See the readme file for more information. 3 | 4 | 'use strict'; 5 | 6 | var app = require('app'); 7 | var BrowserWindow = require('browser-window'); 8 | 9 | // require('crash-reporter').start(); 10 | 11 | var mainWindow = null; 12 | 13 | app.on('window-all-closed', function () { 14 | if (process.platform != 'darwin') { 15 | app.quit(); 16 | } 17 | }); 18 | 19 | app.on('ready', function () { 20 | mainWindow = new BrowserWindow({ 21 | width: 800, 22 | height: 600 23 | }); 24 | 25 | // mainWindow.webContents.openDevTools(); 26 | 27 | mainWindow.loadURL('file://' + __dirname + '/index.html'); 28 | mainWindow.webContents.on('did-finish-load', function () { 29 | mainWindow.setTitle(app.getName()); 30 | }); 31 | mainWindow.on('closed', function () { 32 | mainWindow = null; 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /exar-ui/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '.', 4 | frameworks: ['systemjs', 'jasmine'], 5 | systemjs: { 6 | configFile: 'config.js', 7 | config: { 8 | paths: { 9 | "*": "*", 10 | "src/*": "src/*", 11 | "typescript": "node_modules/typescript/lib/typescript.js", 12 | "systemjs": "node_modules/systemjs/dist/system.js", 13 | 'system-polyfills': 'node_modules/systemjs/dist/system-polyfills.js', 14 | 'es6-module-loader': 'node_modules/es6-module-loader/dist/es6-module-loader.js' 15 | }, 16 | packages: { 17 | 'test/unit': { 18 | defaultExtension: 'ts' 19 | }, 20 | 'src': { 21 | defaultExtension: 'ts' 22 | } 23 | }, 24 | transpiler: 'typescript' 25 | }, 26 | serveFiles: [ 27 | 'src/**/*.ts', 28 | 'jspm_packages/**/*.js' 29 | ] 30 | }, 31 | files: [ 32 | 'test/unit/setup.ts', 33 | 'test/unit/*.ts', 34 | 'test/unit/**/*.ts' 35 | ], 36 | exclude: [], 37 | preprocessors: { }, 38 | reporters: ['spec'], 39 | port: 9876, 40 | colors: true, 41 | logLevel: config.LOG_INFO, 42 | autoWatch: true, 43 | browsers: ['Chrome'], 44 | singleRun: false 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /exar-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exar-ui", 3 | "version": "0.1.0", 4 | "author": "Bruno Filippone ", 5 | "license": "UNLICENSED", 6 | "main": "index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "http://github.com/bfil/exar-ui" 10 | }, 11 | "scripts": { 12 | "test": "gulp test", 13 | "e2e": "gulp serve e2e" 14 | }, 15 | "devDependencies": { 16 | "aurelia-bundler": "^0.4.0", 17 | "aurelia-tools": "^0.2.3", 18 | "browser-sync": "^2.13.0", 19 | "connect-history-api-fallback": "^1.1.0", 20 | "conventional-changelog": "1.1.0", 21 | "del": "^2.2.1", 22 | "es6-module-loader": "^0.17.8", 23 | "gulp": "^3.9.1", 24 | "gulp-bump": "^2.1.0", 25 | "gulp-changed": "^1.3.0", 26 | "gulp-notify": "^2.2.0", 27 | "gulp-plumber": "^1.1.0", 28 | "gulp-protractor": "^2.4.0", 29 | "gulp-sourcemaps": "^1.6.0", 30 | "gulp-tslint": "^5.0.0", 31 | "gulp-typescript": "^2.13.6", 32 | "isparta": "^4.0.0", 33 | "jasmine-core": "^2.4.1", 34 | "jspm": "0.16.15", 35 | "karma": "^0.13.22", 36 | "karma-chrome-launcher": "^1.0.1", 37 | "karma-jasmine": "^1.0.2", 38 | "karma-spec-reporter": "0.0.26", 39 | "karma-systemjs": "^0.14.0", 40 | "object.assign": "^4.0.3", 41 | "require-dir": "^0.3.0", 42 | "run-sequence": "^1.2.1", 43 | "systemjs": "0.19.31", 44 | "tslint": "^3.11.0", 45 | "typescript": "^2.0.0", 46 | "vinyl-paths": "^2.1.0", 47 | "yargs": "^4.7.1" 48 | }, 49 | "jspm": { 50 | "dependencies": { 51 | "aurelia-animator-css": "npm:aurelia-animator-css@^1.0.0", 52 | "aurelia-bootstrapper": "npm:aurelia-bootstrapper@^1.0.0", 53 | "aurelia-dialog": "npm:aurelia-dialog@^1.0.0", 54 | "aurelia-fetch-client": "npm:aurelia-fetch-client@^1.0.0", 55 | "aurelia-framework": "npm:aurelia-framework@^1.0.0", 56 | "aurelia-history-browser": "npm:aurelia-history-browser@^1.0.0", 57 | "aurelia-http-client": "npm:aurelia-http-client@^1.0.0", 58 | "aurelia-loader-default": "npm:aurelia-loader-default@^1.0.0", 59 | "aurelia-logging-console": "npm:aurelia-logging-console@^1.0.0", 60 | "aurelia-pal-browser": "npm:aurelia-pal-browser@^1.0.0", 61 | "aurelia-polyfills": "npm:aurelia-polyfills@^1.0.0", 62 | "aurelia-router": "npm:aurelia-router@^1.0.0", 63 | "aurelia-templating-binding": "npm:aurelia-templating-binding@^1.0.0", 64 | "aurelia-templating-resources": "npm:aurelia-templating-resources@^1.0.0", 65 | "aurelia-templating-router": "npm:aurelia-templating-router@^1.0.0", 66 | "bootstrap": "github:twbs/bootstrap@^3.3.7", 67 | "emailjs-tcp-socket": "npm:emailjs-tcp-socket@^1.0.2", 68 | "fetch": "github:github/fetch@^0.10.1", 69 | "font-awesome": "npm:font-awesome@^4.5.0", 70 | "forge": "npm:forge@^2.3.0", 71 | "rx": "npm:rx@^4.1.0", 72 | "text": "github:systemjs/plugin-text@^0.0.3" 73 | }, 74 | "devDependencies": { 75 | "core-js": "npm:core-js@^2.0.3", 76 | "traceur": "github:jmcriffey/bower-traceur@0.0.92", 77 | "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.92" 78 | }, 79 | "overrides": { 80 | "npm:core-js@2.0.3": { 81 | "main": "client/shim.min" 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /exar-ui/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // An example configuration file. 2 | exports.config = { 3 | directConnect: true, 4 | 5 | // Capabilities to be passed to the webdriver instance. 6 | capabilities: { 7 | 'browserName': 'chrome' 8 | }, 9 | 10 | //seleniumAddress: 'http://0.0.0.0:4444', 11 | // add proper version number 12 | seleniumServerJar: './node_modules/gulp-protractor/node_modules/protractor/selenium/selenium-server-standalone-2.44.0.jar', 13 | specs: ['test/e2e/dist/**/*.js'], 14 | 15 | plugins: [{ 16 | path: 'aurelia.protractor.js' 17 | }], 18 | 19 | 20 | // Options to be passed to Jasmine-node. 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /exar-ui/src/app.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /exar-ui/src/app.ts: -------------------------------------------------------------------------------- 1 | import {Router, RouterConfiguration} from 'aurelia-router'; 2 | 3 | export class App { 4 | router: Router; 5 | 6 | configureRouter(config: RouterConfiguration, router: Router) { 7 | config.title = 'Exar UI'; 8 | config.map([ 9 | { route: '', name: 'home', moduleId: 'views/home', nav: false, title: 'Home' }, 10 | { route: 'manage-connections', name: 'manage-connections', moduleId: 'views/manage-connections', nav: false, title: 'Manage Connections' } 11 | ]); 12 | this.router = router; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /exar-ui/src/components/dialogs/edit-connection.html: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /exar-ui/src/components/dialogs/edit-connection.ts: -------------------------------------------------------------------------------- 1 | import {autoinject} from 'aurelia-framework'; 2 | import {DialogController} from 'aurelia-dialog'; 3 | 4 | import {SavedConnection} from 'models/saved-connection'; 5 | 6 | @autoinject 7 | export class EditConnection { 8 | connection: SavedConnection; 9 | dialogController: DialogController; 10 | 11 | constructor(dialogController: DialogController) { 12 | this.dialogController = dialogController; 13 | } 14 | 15 | activate(connection: SavedConnection) { 16 | this.connection = connection; 17 | } 18 | 19 | saveConnection() { 20 | this.dialogController.ok(this.connection); 21 | } 22 | 23 | cancelConnection() { 24 | this.dialogController.cancel() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /exar-ui/src/components/layout/nav-bar.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /exar-ui/src/components/layout/nav-bar.ts: -------------------------------------------------------------------------------- 1 | import {autoinject, bindable} from 'aurelia-framework'; 2 | import {Router} from 'aurelia-router'; 3 | 4 | @autoinject 5 | export class NavBar { 6 | 7 | @bindable router: Router; 8 | 9 | element: Element; 10 | 11 | constructor(element: Element) { 12 | this.element = element; 13 | } 14 | } -------------------------------------------------------------------------------- /exar-ui/src/converters/obfuscate.ts: -------------------------------------------------------------------------------- 1 | export class ObfuscateValueConverter { 2 | toView(value) { 3 | if(value) return Array(value.length).join('*'); 4 | else return ''; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /exar-ui/src/exar/client.ts: -------------------------------------------------------------------------------- 1 | import {Connection, Event, Query} from 'exar/model'; 2 | import {Connect, Connected, Publish, Published, Subscribe, Subscribed, DatabaseError, TcpMessage} from 'exar/net'; 3 | 4 | import * as Rx from 'rx'; 5 | 6 | export class ExarClient { 7 | 8 | private socket: TCPSocket; 9 | private socketObservable: Rx.ControlledObservable; 10 | 11 | private encoder: TextEncoding.TextEncoder; 12 | private decoder: TextEncoding.TextDecoder; 13 | 14 | constructor() { 15 | this.encoder = new TextEncoder("utf8"); 16 | this.decoder = new TextDecoder("utf8"); 17 | } 18 | 19 | private encode(data: string): ArrayBufferView { 20 | return this.encoder.encode(data); 21 | } 22 | 23 | private decode(data: ArrayBufferView): string { 24 | return this.decoder.decode(data); 25 | } 26 | 27 | private send(message: TcpMessage) { 28 | this.socket.send(this.encode(message.toTabSeparatedString())); 29 | } 30 | 31 | private requestSubscription: Rx.IDisposable; 32 | private request(message: TcpMessage, handleResponse: (message: string) => T, sendOnOpen: boolean = false) { 33 | return new Promise((resolve, reject) => { 34 | if(this.requestSubscription) this.requestSubscription.dispose(); 35 | this.requestSubscription = this.socketObservable.subscribe(message => { 36 | resolve(handleResponse(message)); 37 | }, reject); 38 | if(sendOnOpen) this.socket.onopen = () => this.send(message); 39 | else this.send(message); 40 | this.socketObservable.request(1); 41 | }); 42 | } 43 | 44 | private createSocketObservable() { 45 | this.socketObservable = Rx.Observable.create(observer => { 46 | this.socket.ondata = message => { 47 | let messages = this.decode(message.data).split('\n').filter(m => !!m); 48 | for(let message of messages) { 49 | if (message.startsWith('Error')) { 50 | observer.onError(DatabaseError.fromTabSeparatedString(message)); 51 | this.createSocketObservable(); 52 | } 53 | else if (message) observer.onNext(message); 54 | } 55 | }; 56 | this.socket.onerror = error => observer.onError(error.data); 57 | }).controlled(); 58 | } 59 | 60 | connect(connectionInfo: Connection) { 61 | this.socket = navigator.TCPSocket.open(connectionInfo.host, connectionInfo.port); 62 | this.createSocketObservable(); 63 | return this.request( 64 | new Connect(connectionInfo.collection, connectionInfo.username, connectionInfo.password), 65 | Connected.fromTabSeparatedString, true); 66 | } 67 | 68 | onDisconnect(onDisconnect: () => any) { 69 | this.socket.onclose = onDisconnect; 70 | } 71 | 72 | disconnect() { 73 | this.socket.close(); 74 | } 75 | 76 | publish(event: Event) { 77 | return this.request(new Publish(event), Published.fromTabSeparatedString); 78 | } 79 | 80 | subscribe(query: Query) { 81 | return this.request(new Subscribe(query), message => { 82 | return Rx.Observable.create(observer => { 83 | let subscription = this.socketObservable.subscribe(message => { 84 | if (message === 'EndOfEventStream') { 85 | observer.onCompleted(); 86 | subscription.dispose(); 87 | } else { 88 | observer.onNext(Event.fromTabSeparatedString(message)); 89 | this.socketObservable.request(1); 90 | } 91 | }); 92 | this.socketObservable.request(1); 93 | }); 94 | }); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /exar-ui/src/exar/model.ts: -------------------------------------------------------------------------------- 1 | import {TcpMessage, TcpMessageEncoder, TcpMessageDecoder} from 'exar/net'; 2 | 3 | export class Connection { 4 | host: string; 5 | port: number; 6 | username: string; 7 | password: string; 8 | collection: string; 9 | 10 | constructor(collection: string, host: string = 'localhost', port: number = 38580, username?: string, password?: string) { 11 | this.collection = collection; 12 | this.host = host; 13 | this.port = port; 14 | this.username = username; 15 | this.password = password; 16 | } 17 | } 18 | 19 | export class Event implements TcpMessage { 20 | id: number = 0; 21 | tags: string[]; 22 | timestamp: number = 0; 23 | data: string; 24 | 25 | constructor(data: string, tags: string[]) { 26 | this.data = data; 27 | this.tags = tags; 28 | } 29 | 30 | withId(id: number) { 31 | this.id = id; 32 | return this; 33 | } 34 | 35 | withTimestamp(timestamp: number) { 36 | this.timestamp = timestamp; 37 | return this; 38 | } 39 | 40 | toTabSeparatedString(): string { 41 | return TcpMessageEncoder.toTabSeparatedString('Event', 42 | this.id || 0, 43 | this.timestamp || 0, 44 | this.tags.join(' '), 45 | this.data); 46 | } 47 | 48 | static fromTabSeparatedString(data: string): Event { 49 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 5); 50 | let id = parseInt(messageParts[1]); 51 | let timestamp = parseInt(messageParts[2]); 52 | let tags = messageParts[3].split(' '); 53 | let eventData = messageParts[4]; 54 | return new Event(eventData, tags).withId(id).withTimestamp(timestamp); 55 | } 56 | } 57 | 58 | export class Query { 59 | liveStream: boolean; 60 | offset: number; 61 | limit: number; 62 | tag: string; 63 | 64 | constructor(liveStream: boolean, offset: number = 0, limit: number = 0, tag?: string) { 65 | this.liveStream = liveStream; 66 | this.offset = offset; 67 | this.limit = limit; 68 | this.tag = tag; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /exar-ui/src/exar/net.ts: -------------------------------------------------------------------------------- 1 | import {Event, Query} from 'exar/model'; 2 | 3 | export class TcpMessageEncoder { 4 | static toTabSeparatedString(...args): string { 5 | return args.filter(arg => typeof arg !== 'undefined').join('\t') + '\n'; 6 | } 7 | } 8 | 9 | export class TcpMessageDecoder { 10 | static parseTabSeparatedString(data: string, numberOfParts: number): string[] { 11 | return data.split('\t', numberOfParts); 12 | } 13 | } 14 | 15 | export interface TcpMessage { 16 | toTabSeparatedString(): string; 17 | } 18 | 19 | export class Connect implements TcpMessage { 20 | 21 | private collection: string; 22 | private username: string; 23 | private password: string; 24 | 25 | constructor(collection: string, username?: string, password?: string) { 26 | this.collection = collection; 27 | this.username = username; 28 | this.password = password; 29 | } 30 | 31 | toTabSeparatedString() { 32 | return TcpMessageEncoder.toTabSeparatedString('Connect', 33 | this.collection, 34 | this.username, 35 | this.password); 36 | } 37 | 38 | static fromTabSeparatedString(data: string) { 39 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 4); 40 | let collection = messageParts[1]; 41 | let username = messageParts[2]; 42 | let password = messageParts[3]; 43 | return new Connect(collection, username, password); 44 | } 45 | } 46 | 47 | export class Connected implements TcpMessage { 48 | 49 | toTabSeparatedString() { 50 | return TcpMessageEncoder.toTabSeparatedString('Connected'); 51 | } 52 | 53 | static fromTabSeparatedString(data: string) { 54 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 1); 55 | return new Connected(); 56 | } 57 | } 58 | 59 | export class Publish implements TcpMessage { 60 | 61 | private event: Event; 62 | 63 | constructor(event: Event) { 64 | this.event = event; 65 | } 66 | 67 | toTabSeparatedString() { 68 | return TcpMessageEncoder.toTabSeparatedString('Publish', 69 | this.event.tags.join(' '), 70 | this.event.timestamp, 71 | this.event.data); 72 | } 73 | 74 | static fromTabSeparatedString(data: string) { 75 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 4); 76 | let tags = messageParts[1].split(' '); 77 | let timestamp = parseInt(messageParts[2]); 78 | let eventData = messageParts[3]; 79 | let event = new Event(eventData, tags).withTimestamp(timestamp); 80 | return new Publish(event); 81 | } 82 | } 83 | 84 | export class Published implements TcpMessage { 85 | 86 | private eventId: number; 87 | 88 | constructor(eventId: number) { 89 | this.eventId = eventId; 90 | } 91 | 92 | toTabSeparatedString() { 93 | return TcpMessageEncoder.toTabSeparatedString('Published', this.eventId); 94 | } 95 | 96 | static fromTabSeparatedString(data: string) { 97 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 2); 98 | let eventId = parseInt(messageParts[1]); 99 | return new Published(eventId); 100 | } 101 | } 102 | 103 | export class Subscribe implements TcpMessage { 104 | 105 | private query: Query; 106 | 107 | constructor(query: Query) { 108 | this.query = query; 109 | } 110 | 111 | toTabSeparatedString() { 112 | return TcpMessageEncoder.toTabSeparatedString('Subscribe', 113 | this.query.liveStream, 114 | this.query.offset || 0, 115 | this.query.limit || 0, 116 | this.query.tag); 117 | } 118 | 119 | static fromTabSeparatedString(data: string) { 120 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 5); 121 | let liveStream = messageParts[1] === 'true'; 122 | let offset = parseInt(messageParts[2]); 123 | let limit = parseInt(messageParts[3]); 124 | let tag = messageParts[4]; 125 | let query = new Query(liveStream, offset, limit, tag); 126 | return new Subscribe(query); 127 | } 128 | } 129 | 130 | export class Subscribed implements TcpMessage { 131 | 132 | toTabSeparatedString() { 133 | return TcpMessageEncoder.toTabSeparatedString('Subscribed'); 134 | } 135 | 136 | static fromTabSeparatedString(data: string) { 137 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 1); 138 | return new Subscribed(); 139 | } 140 | } 141 | 142 | export class DatabaseError implements TcpMessage { 143 | 144 | private type: string; 145 | private subType: string; 146 | private data: string; 147 | 148 | constructor(type: string, data: string, subType?: string) { 149 | this.type = type; 150 | this.subType = subType; 151 | this.data = data; 152 | } 153 | 154 | toTabSeparatedString() { 155 | return TcpMessageEncoder.toTabSeparatedString('Error', this.type, this.subType, this.data); 156 | } 157 | 158 | toString() { 159 | if(this.type === 'ParseError' && this.subType === 'MissingField') { 160 | return `${this.type}: missing field at position ${this.data}`; 161 | } else if(this.type === 'AuthenticationError') { 162 | return `${this.type}: missing or invalid credentials`; 163 | } return `${this.type}: ${this.data}`; 164 | } 165 | 166 | static fromTabSeparatedString(data: string) { 167 | let messageParts = TcpMessageDecoder.parseTabSeparatedString(data, 4); 168 | let errorData = messageParts[3] || messageParts[2]; 169 | let subType = messageParts[3] ? messageParts[2] : undefined; 170 | return new DatabaseError(messageParts[1], errorData, subType); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /exar-ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import 'bootstrap'; 2 | import 'emailjs-tcp-socket'; 3 | 4 | import {Aurelia} from 'aurelia-framework'; 5 | 6 | export function configure(aurelia: Aurelia) { 7 | aurelia.use 8 | .standardConfiguration() 9 | .developmentLogging() 10 | .plugin('aurelia-animator-css') 11 | .plugin('aurelia-dialog', config => { 12 | config.useDefaults(); 13 | config.settings.lock = false; 14 | }) 15 | .globalResources([ 16 | 'converters/obfuscate' 17 | ]); 18 | 19 | aurelia.start().then(() => aurelia.setRoot()); 20 | } 21 | -------------------------------------------------------------------------------- /exar-ui/src/models/saved-connection.ts: -------------------------------------------------------------------------------- 1 | export class SavedConnection { 2 | alias: string; 3 | requiresAuth: boolean; 4 | host: string = 'localhost'; 5 | port: number = 38580; 6 | username: string; 7 | password: string; 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/src/views/connection-handler.html: -------------------------------------------------------------------------------- 1 | 75 | -------------------------------------------------------------------------------- /exar-ui/src/views/connection-handler.ts: -------------------------------------------------------------------------------- 1 | import {autoinject, bindable} from 'aurelia-framework'; 2 | 3 | import {ExarClient} from 'exar/client'; 4 | import {Connection, Event, Query} from 'exar/model'; 5 | import {TcpMessage} from 'exar/net'; 6 | 7 | import {SavedConnection} from 'models/saved-connection'; 8 | 9 | import * as Rx from 'rx'; 10 | 11 | @autoinject 12 | export class ConnectionHandler { 13 | exarClient: ExarClient; 14 | 15 | savedConnections: SavedConnection[]; 16 | @bindable connection: SavedConnection; 17 | @bindable collection: string; 18 | 19 | data: string; 20 | tags: string; 21 | 22 | liveStream: boolean = false; 23 | offset: string; 24 | limit: string; 25 | tag: string; 26 | 27 | connected: boolean; 28 | subscription: Rx.IDisposable; 29 | messages: { payload: string, className: string }[]; 30 | 31 | bind() { 32 | this.savedConnections = localStorage.getItem('connections.saved') ? JSON.parse(localStorage.getItem('connections.saved')) : []; 33 | if(this.savedConnections.length) { 34 | this.connection = this.savedConnections[0]; 35 | } 36 | this.connected = false; 37 | this.messages = []; 38 | } 39 | 40 | unbind() { 41 | if(this.exarClient) this.disconnect(); 42 | } 43 | 44 | connect(isReconnection: boolean = false) { 45 | this.exarClient = new ExarClient(); 46 | this.exarClient.connect(this.initializeConnection(this.collection, this.connection)) 47 | .then(connected => { 48 | this.connected = true; 49 | if(!isReconnection) this.logTcpMessage(connected); 50 | }, this.onError.bind(this)); 51 | this.exarClient.onDisconnect(() => { 52 | this.connected = false; 53 | if(!isReconnection) this.logMessage(`Disconnected`, false); 54 | }); 55 | } 56 | 57 | disconnect() { 58 | this.exarClient.disconnect(); 59 | } 60 | 61 | reconnect() { 62 | this.disconnect(); 63 | this.connect(true); 64 | } 65 | 66 | publish() { 67 | let event = new Event(this.data, (this.tags || '').split(' ')); 68 | this.exarClient.publish(event).then( 69 | published => this.logTcpMessage(published), 70 | this.onError.bind(this) 71 | ) 72 | } 73 | 74 | subscribe() { 75 | let query = new Query(this.liveStream, parseInt(this.offset), parseInt(this.limit), this.tag); 76 | this.exarClient.subscribe(query).then( 77 | eventStream => { 78 | this.logMessage('Subscribed', false); 79 | this.subscription = eventStream.subscribe( 80 | this.logTcpMessage.bind(this), 81 | this.onError.bind(this), 82 | () => { 83 | this.logMessage('EndOfEventStream', false); 84 | this.subscription = undefined; 85 | } 86 | ); 87 | }, 88 | this.onError.bind(this) 89 | ) 90 | } 91 | 92 | unsubscribe() { 93 | if(this.subscription) { 94 | this.subscription.dispose(); 95 | this.subscription = undefined; 96 | this.logMessage('EndOfEventStream', false); 97 | this.reconnect(); 98 | } 99 | } 100 | 101 | selectConnection(connection: SavedConnection) { 102 | this.connection = connection; 103 | } 104 | 105 | onError(error: any) { 106 | this.logMessage(error.toString(), true); 107 | this.unsubscribe(); 108 | } 109 | 110 | logMessage(message: string, isError: boolean) { 111 | this.messages.push({ 112 | payload: message, 113 | className: isError ? 'text-danger' : '' 114 | }); 115 | } 116 | 117 | logTcpMessage(message: TcpMessage) { 118 | this.logMessage(message.toTabSeparatedString(), false); 119 | } 120 | 121 | clearMessages() { 122 | this.messages = []; 123 | } 124 | 125 | initializeConnection(collection: string, connection: SavedConnection): Connection { 126 | let username = connection.requiresAuth ? connection.username : undefined; 127 | let password = connection.requiresAuth ? connection.password : undefined; 128 | return new Connection(collection, connection.host, connection.port, username, password); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /exar-ui/src/views/home.html: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /exar-ui/src/views/home.ts: -------------------------------------------------------------------------------- 1 | import {autoinject} from 'aurelia-framework'; 2 | 3 | import * as $ from 'jquery'; 4 | 5 | import {SavedConnection} from 'models/saved-connection'; 6 | 7 | @autoinject 8 | export class Home { 9 | tabs: Tab[] = []; 10 | 11 | addTab() { 12 | this.tabs.push(new Tab()); 13 | this.showTab(this.tabs.length - 1); 14 | } 15 | 16 | removeTab(index, event) { 17 | this.tabs.splice(index, 1); 18 | event.stopPropagation(); 19 | let selectedTabIndex = this.getSelectedTabIndex(); 20 | if(index > selectedTabIndex) this.showTab(selectedTabIndex); 21 | else if(index < selectedTabIndex) this.showTab(selectedTabIndex - 1); 22 | } 23 | 24 | showTab(index) { 25 | setTimeout(() => $(`#tab-${index}`).tab('show')); 26 | } 27 | 28 | getSelectedTabIndex() { 29 | return Number($('.tab-pane.active').attr('id').replace('tab-content-', '')); 30 | } 31 | } 32 | 33 | class Tab { 34 | collection: string; 35 | connection: SavedConnection; 36 | 37 | get name() { 38 | if(this.collection) { 39 | return `${this.collection} @ ${this.connection.alias}`; 40 | } else { 41 | return 'New connection'; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exar-ui/src/views/manage-connections.html: -------------------------------------------------------------------------------- 1 | 38 | -------------------------------------------------------------------------------- /exar-ui/src/views/manage-connections.ts: -------------------------------------------------------------------------------- 1 | import {autoinject} from 'aurelia-framework'; 2 | import {DialogService} from 'aurelia-dialog'; 3 | 4 | import {SavedConnection} from 'models/saved-connection'; 5 | import {EditConnection} from 'components/dialogs/edit-connection'; 6 | 7 | @autoinject 8 | export class ManageConnections { 9 | savedConnections: SavedConnection[]; 10 | dialogService: DialogService; 11 | 12 | constructor(dialogService: DialogService) { 13 | let storedConnections = localStorage.getItem('connections.saved'); 14 | this.savedConnections = storedConnections ? JSON.parse(storedConnections) : []; 15 | this.dialogService = dialogService; 16 | } 17 | 18 | newConnection() { 19 | this.dialogService.open({ viewModel: EditConnection, model: new SavedConnection() }) 20 | .then(result => { 21 | if(result.output) this.saveConnection(result.output) 22 | }); 23 | } 24 | 25 | editConnection(connection) { 26 | this.dialogService.open({ viewModel: EditConnection, model: connection }) 27 | .then(result => { 28 | if(result.output) this.saveConnection(result.output) 29 | }); 30 | } 31 | 32 | deleteConnection(connection) { 33 | let index = this.savedConnections.indexOf(connection); 34 | this.savedConnections.splice(index, 1); 35 | localStorage.setItem('connections.saved', JSON.stringify(this.savedConnections)); 36 | } 37 | 38 | saveConnection(connection: SavedConnection) { 39 | if(this.savedConnections.indexOf(connection) === -1) { 40 | this.savedConnections.push(connection); 41 | } 42 | localStorage.setItem('connections.saved', JSON.stringify(this.savedConnections)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exar-ui/styles/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .splash { 6 | text-align: center; 7 | margin: 10% 0 0 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | .splash .message { 12 | font-size: 24px; 13 | line-height: 24px; 14 | text-shadow: rgba(0, 0, 0, 0.5) 0 0 15px; 15 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 16 | } 17 | 18 | .splash .fa-spinner { 19 | text-align: center; 20 | display: inline-block; 21 | font-size: 34px; 22 | margin-top: 50px; 23 | } 24 | 25 | .page-host { 26 | position: absolute; 27 | left: 0; 28 | right: 0; 29 | top: 50px; 30 | bottom: 0; 31 | overflow-x: hidden; 32 | overflow-y: auto; 33 | } 34 | 35 | @media print { 36 | .page-host { 37 | position: absolute; 38 | left: 10px; 39 | right: 0; 40 | top: 50px; 41 | bottom: 0; 42 | overflow-y: inherit; 43 | overflow-x: inherit; 44 | } 45 | } 46 | 47 | section { 48 | margin: 0 20px; 49 | } 50 | 51 | .navbar-header { 52 | width: 100%; 53 | } 54 | 55 | .navbar-nav { 56 | margin: 0px; 57 | } 58 | 59 | .navbar-nav li.loader { 60 | margin: 12px 6px 0 6px; 61 | } 62 | 63 | ul.nav-tabs li i.fa { 64 | cursor: pointer; 65 | } 66 | 67 | section.au-enter-active { 68 | -webkit-animation: fadeInRight 1s; 69 | animation: fadeInRight 1s; 70 | } 71 | 72 | div.au-stagger { 73 | -webkit-animation-delay: 50ms; 74 | animation-delay: 50ms; 75 | } 76 | 77 | h3 { 78 | margin-bottom: 20px; 79 | } 80 | -------------------------------------------------------------------------------- /exar-ui/test/unit/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { Container } from 'aurelia-framework'; 2 | import { Router, RouterConfiguration } from 'aurelia-router'; 3 | 4 | import { App } from 'src/app'; 5 | 6 | describe('App', () => { 7 | var app: App, 8 | routerConfig: RouterConfiguration, 9 | router: Router; 10 | 11 | beforeEach(() => { 12 | let container = new Container(); 13 | 14 | router = container.get(Router); 15 | routerConfig = container.get(RouterConfiguration); 16 | 17 | spyOn(routerConfig, 'map'); 18 | 19 | app = new App(); 20 | }); 21 | 22 | it('should configure the router correctly', () => { 23 | app.configureRouter(routerConfig, router); 24 | 25 | expect(app.router).toBeDefined(); 26 | expect(routerConfig.title).toEqual('Exar UI'); 27 | expect(routerConfig.map).toHaveBeenCalled(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /exar-ui/test/unit/setup.ts: -------------------------------------------------------------------------------- 1 | import {initialize} from 'aurelia-pal-browser' 2 | import 'aurelia-polyfills' 3 | 4 | initialize(); 5 | -------------------------------------------------------------------------------- /exar-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "rootDir": "src/", 6 | "sourceMap": true, 7 | "target": "es5", 8 | "module": "amd", 9 | "declaration": false, 10 | "noImplicitAny": false, 11 | "removeComments": true, 12 | "emitDecoratorMetadata": true, 13 | "experimentalDecorators": true, 14 | "moduleResolution": "classic", 15 | "lib": ["es2015", "dom"] 16 | }, 17 | "filesGlob": [ 18 | "src/**/*.ts", 19 | "test/**/*.ts", 20 | "custom_typings/**/*.d.ts", 21 | "typings/index.d.ts" 22 | ], 23 | "exclude": [ 24 | "node_modules", 25 | "jspm_packages/npm/react-tools@0.13.3/src/modern/class", 26 | "jspm_packages/npm/rx@4.1.0/ts", 27 | "Exar UI-darwin-x64" 28 | ], 29 | "atom": { 30 | "rewriteTsconfig": false 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /exar-ui/tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "es6-promise/es6-promise.d.ts": { 9 | "commit": "24e12a7af994afa2226af70fbfae0adc78736a9e" 10 | }, 11 | "jasmine/jasmine.d.ts": { 12 | "commit": "16cfc9e52eec782859fc91b413e9866640651395" 13 | }, 14 | "angular-protractor/angular-protractor.d.ts": { 15 | "commit": "24e12a7af994afa2226af70fbfae0adc78736a9e" 16 | }, 17 | "selenium-webdriver/selenium-webdriver.d.ts": { 18 | "commit": "24e12a7af994afa2226af70fbfae0adc78736a9e" 19 | }, 20 | "jquery/jquery.d.ts": { 21 | "commit": "9f0f926a12026287b5a4a229e5672c01e7549313" 22 | }, 23 | "text-encoding/text-encoding.d.ts": { 24 | "commit": "0d622d857f97d44ea7dcad2b3edec1f23c48fe9e" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /exar-ui/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /exar-ui/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exar-ui", 3 | "globalDevDependencies": { 4 | "angular-protractor": "registry:dt/angular-protractor", 5 | "aurelia-protractor": "github:aurelia/typings/dist/aurelia-protractor.d.ts", 6 | "jasmine": "registry:dt/jasmine", 7 | "selenium-webdriver": "registry:dt/selenium-webdriver" 8 | }, 9 | "globalDependencies": { 10 | "bootstrap": "registry:dt/bootstrap", 11 | "jquery": "registry:dt/jquery", 12 | "url": "github:aurelia/fetch-client/doc/url.d.ts", 13 | "whatwg-fetch": "registry:dt/whatwg-fetch" 14 | }, 15 | "dependencies": { 16 | "aurelia-animator-css": "github:aurelia/animator-css", 17 | "aurelia-binding": "github:aurelia/binding", 18 | "aurelia-bootstrapper": "github:aurelia/bootstrapper", 19 | "aurelia-bootstrapper-webpack": "github:aurelia/bootstrapper-webpack", 20 | "aurelia-dependency-injection": "github:aurelia/dependency-injection", 21 | "aurelia-dialog": "github:aurelia/dialog", 22 | "aurelia-event-aggregator": "github:aurelia/event-aggregator", 23 | "aurelia-fetch-client": "github:aurelia/fetch-client", 24 | "aurelia-framework": "github:aurelia/framework", 25 | "aurelia-history": "github:aurelia/history", 26 | "aurelia-history-browser": "github:aurelia/history-browser", 27 | "aurelia-loader": "github:aurelia/loader", 28 | "aurelia-loader-webpack": "github:aurelia/loader-webpack", 29 | "aurelia-logging": "github:aurelia/logging", 30 | "aurelia-logging-console": "github:aurelia/logging-console", 31 | "aurelia-metadata": "github:aurelia/metadata", 32 | "aurelia-pal": "github:aurelia/pal", 33 | "aurelia-pal-browser": "github:aurelia/pal-browser", 34 | "aurelia-path": "github:aurelia/path", 35 | "aurelia-polyfills": "github:aurelia/polyfills", 36 | "aurelia-route-recognizer": "github:aurelia/route-recognizer", 37 | "aurelia-router": "github:aurelia/router", 38 | "aurelia-task-queue": "github:aurelia/task-queue", 39 | "aurelia-templating": "github:aurelia/templating", 40 | "aurelia-templating-binding": "github:aurelia/templating-binding", 41 | "aurelia-templating-resources": "github:aurelia/templating-resources", 42 | "aurelia-templating-router": "github:aurelia/templating-router" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/angular-protractor/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f9c44651705f574f6d4258fe5e1c335462bdcc19/angular-protractor/angular-protractor.d.ts", 5 | "raw": "registry:dt/angular-protractor#1.5.0+20160425143459", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/f9c44651705f574f6d4258fe5e1c335462bdcc19/angular-protractor/angular-protractor.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/aurelia-protractor/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/typings/master/dist/aurelia-protractor.d.ts 3 | declare module protractor { 4 | interface IBrowser extends protractor.Protractor { 5 | loadAndWaitForAureliaPage(url: string): protractor.Protractor; 6 | waitForRouterComplete(); 7 | } 8 | 9 | interface IProtractorLocatorStrategy { 10 | valueBind(bindTarget: string): webdriver.Locator; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/aurelia-protractor/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/typings/master/dist/aurelia-protractor.d.ts", 5 | "raw": "github:aurelia/typings/dist/aurelia-protractor.d.ts", 6 | "typings": "https://raw.githubusercontent.com/aurelia/typings/master/dist/aurelia-protractor.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/bootstrap/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/1f0791147c6c145227b1778bb26781a79d516917/bootstrap/bootstrap.d.ts 3 | interface ModalOptions { 4 | backdrop?: boolean|string; 5 | keyboard?: boolean; 6 | show?: boolean; 7 | remote?: string; 8 | } 9 | 10 | interface ModalOptionsBackdropString { 11 | backdrop?: string; // for "static" 12 | keyboard?: boolean; 13 | show?: boolean; 14 | remote?: string; 15 | } 16 | 17 | interface ScrollSpyOptions { 18 | offset?: number; 19 | target?: string; 20 | } 21 | 22 | interface TooltipOptions { 23 | animation?: boolean; 24 | html?: boolean; 25 | placement?: string | Function; 26 | selector?: string; 27 | title?: string | Function; 28 | trigger?: string; 29 | template?: string; 30 | delay?: number | Object; 31 | container?: string | boolean; 32 | viewport?: string | Function | Object; 33 | } 34 | 35 | interface PopoverOptions { 36 | animation?: boolean; 37 | html?: boolean; 38 | placement?: string | Function; 39 | selector?: string; 40 | trigger?: string; 41 | title?: string | Function; 42 | template?: string; 43 | content?: any; 44 | delay?: number | Object; 45 | container?: string | boolean; 46 | viewport?: string | Function | Object; 47 | } 48 | 49 | interface CollapseOptions { 50 | parent?: any; 51 | toggle?: boolean; 52 | } 53 | 54 | interface CarouselOptions { 55 | interval?: number; 56 | pause?: string; 57 | wrap?: boolean; 58 | keybord?: boolean; 59 | } 60 | 61 | interface TypeaheadOptions { 62 | source?: any; 63 | items?: number; 64 | minLength?: number; 65 | matcher?: (item: any) => boolean; 66 | sorter?: (items: any[]) => any[]; 67 | updater?: (item: any) => any; 68 | highlighter?: (item: any) => string; 69 | } 70 | 71 | interface AffixOptions { 72 | offset?: number | Function | Object; 73 | target?: any; 74 | } 75 | 76 | interface TransitionEventNames { 77 | end: string; 78 | } 79 | 80 | interface JQuery { 81 | modal(options?: ModalOptions): JQuery; 82 | modal(options?: ModalOptionsBackdropString): JQuery; 83 | modal(command: string): JQuery; 84 | 85 | dropdown(): JQuery; 86 | dropdown(command: string): JQuery; 87 | 88 | scrollspy(command: string): JQuery; 89 | scrollspy(options?: ScrollSpyOptions): JQuery; 90 | 91 | tab(): JQuery; 92 | tab(command: string): JQuery; 93 | 94 | tooltip(options?: TooltipOptions): JQuery; 95 | tooltip(command: string): JQuery; 96 | 97 | popover(options?: PopoverOptions): JQuery; 98 | popover(command: string): JQuery; 99 | 100 | alert(): JQuery; 101 | alert(command: string): JQuery; 102 | 103 | button(): JQuery; 104 | button(command: string): JQuery; 105 | 106 | collapse(options?: CollapseOptions): JQuery; 107 | collapse(command: string): JQuery; 108 | 109 | carousel(options?: CarouselOptions): JQuery; 110 | carousel(command: string): JQuery; 111 | 112 | typeahead(options?: TypeaheadOptions): JQuery; 113 | 114 | affix(options?: AffixOptions): JQuery; 115 | 116 | emulateTransitionEnd(duration: number): JQuery; 117 | } 118 | 119 | interface JQuerySupport { 120 | transition: boolean | TransitionEventNames; 121 | } 122 | 123 | declare module "bootstrap" { 124 | } 125 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/bootstrap/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/1f0791147c6c145227b1778bb26781a79d516917/bootstrap/bootstrap.d.ts", 5 | "raw": "registry:dt/bootstrap#3.3.5+20160619023404", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/1f0791147c6c145227b1778bb26781a79d516917/bootstrap/bootstrap.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/jasmine/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/c49913aa9ea419ea46c1c684e488cf2a10303b1a/jasmine/jasmine.d.ts", 5 | "raw": "registry:dt/jasmine#2.2.0+20160621224255", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/c49913aa9ea419ea46c1c684e488cf2a10303b1a/jasmine/jasmine.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/jquery/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/6e54cb627506cf64d6effba1fe49b5a091ac4297/jquery/jquery.d.ts", 5 | "raw": "registry:dt/jquery#1.10.0+20160704162008", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/6e54cb627506cf64d6effba1fe49b5a091ac4297/jquery/jquery.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/selenium-webdriver/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/selenium-webdriver/selenium-webdriver.d.ts", 5 | "raw": "registry:dt/selenium-webdriver#2.44.0+20160317120654", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/7de6c3dd94feaeb21f20054b9f30d5dabc5efabd/selenium-webdriver/selenium-webdriver.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/url/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/fetch-client/master/doc/url.d.ts 3 | declare class URLSearchParams { 4 | append(name: string, value: string): void; 5 | delete(name: string):void; 6 | get(name: string): string; 7 | getAll(name: string): Array; 8 | has(name: string): boolean; 9 | set(name: string, value: string): void; 10 | } 11 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/url/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/fetch-client/master/doc/url.d.ts", 5 | "raw": "github:aurelia/fetch-client/doc/url.d.ts", 6 | "typings": "https://raw.githubusercontent.com/aurelia/fetch-client/master/doc/url.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/whatwg-fetch/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/d997f92b892040860145a9510504dff22f672a36/whatwg-fetch/whatwg-fetch.d.ts 3 | declare class Request extends Body { 4 | constructor(input: string|Request, init?:RequestInit); 5 | method: string; 6 | url: string; 7 | headers: Headers; 8 | context: RequestContext; 9 | referrer: string; 10 | mode: RequestMode; 11 | redirect: RequestRedirect; 12 | credentials: RequestCredentials; 13 | cache: RequestCache; 14 | } 15 | 16 | interface RequestInit { 17 | method?: string; 18 | headers?: HeaderInit|{ [index: string]: string }; 19 | body?: BodyInit; 20 | mode?: RequestMode; 21 | redirect?: RequestRedirect; 22 | credentials?: RequestCredentials; 23 | cache?: RequestCache; 24 | } 25 | 26 | type RequestContext = 27 | "audio" | "beacon" | "cspreport" | "download" | "embed" | 28 | "eventsource" | "favicon" | "fetch" | "font" | "form" | "frame" | 29 | "hyperlink" | "iframe" | "image" | "imageset" | "import" | 30 | "internal" | "location" | "manifest" | "object" | "ping" | "plugin" | 31 | "prefetch" | "script" | "serviceworker" | "sharedworker" | 32 | "subresource" | "style" | "track" | "video" | "worker" | 33 | "xmlhttprequest" | "xslt"; 34 | type RequestMode = "same-origin" | "no-cors" | "cors"; 35 | type RequestRedirect = "follow" | "error" | "manual"; 36 | type RequestCredentials = "omit" | "same-origin" | "include"; 37 | type RequestCache = 38 | "default" | "no-store" | "reload" | "no-cache" | 39 | "force-cache" | "only-if-cached"; 40 | 41 | declare interface HeadersMap { 42 | [index: string]: string; 43 | } 44 | 45 | declare class Headers { 46 | constructor(headers?:Headers|HeadersMap) 47 | append(name: string, value: string): void; 48 | delete(name: string):void; 49 | get(name: string): string; 50 | getAll(name: string): Array; 51 | has(name: string): boolean; 52 | set(name: string, value: string): void; 53 | forEach(callback: (value: string, name: string) => void): void; 54 | } 55 | 56 | declare class Body { 57 | bodyUsed: boolean; 58 | arrayBuffer(): Promise; 59 | blob(): Promise; 60 | formData(): Promise; 61 | json(): Promise; 62 | json(): Promise; 63 | text(): Promise; 64 | } 65 | 66 | declare class Response extends Body { 67 | constructor(body?: BodyInit, init?: ResponseInit); 68 | static error(): Response; 69 | static redirect(url: string, status: number): Response; 70 | type: ResponseType; 71 | url: string; 72 | status: number; 73 | ok: boolean; 74 | statusText: string; 75 | headers: Headers; 76 | clone(): Response; 77 | } 78 | 79 | type ResponseType = "basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"; 80 | 81 | interface ResponseInit { 82 | status: number; 83 | statusText?: string; 84 | headers?: HeaderInit; 85 | } 86 | 87 | declare type HeaderInit = Headers|Array; 88 | declare type BodyInit = ArrayBuffer|ArrayBufferView|Blob|FormData|string; 89 | declare type RequestInfo = Request|string; 90 | 91 | interface Window { 92 | fetch(url: string|Request, init?: RequestInit): Promise; 93 | } 94 | 95 | declare var fetch: typeof window.fetch; 96 | -------------------------------------------------------------------------------- /exar-ui/typings/globals/whatwg-fetch/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/d997f92b892040860145a9510504dff22f672a36/whatwg-fetch/whatwg-fetch.d.ts", 5 | "raw": "registry:dt/whatwg-fetch#0.0.0+20160728142841", 6 | "typings": "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/d997f92b892040860145a9510504dff22f672a36/whatwg-fetch/whatwg-fetch.d.ts" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exar-ui/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | /// 6 | /// 7 | /// 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | /// 18 | /// 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-animator-css/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/animator-css/master/dist/aurelia-animator-css.d.ts 3 | declare module 'aurelia-animator-css' { 4 | import { 5 | animationEvent, 6 | TemplatingEngine 7 | } from 'aurelia-templating'; 8 | import { 9 | DOM 10 | } from 'aurelia-pal'; 11 | export interface CssAnimation { 12 | className: string; 13 | element: HTMLElement; 14 | } 15 | 16 | /** 17 | * An implementation of the Animator using CSS3-Animations. 18 | */ 19 | /** 20 | * An implementation of the Animator using CSS3-Animations. 21 | */ 22 | export class CssAnimator { 23 | 24 | /** 25 | * Creates an instance of CssAnimator. 26 | */ 27 | constructor(); 28 | 29 | /* Public API Begin */ 30 | /** 31 | * Execute a single animation. 32 | * @param element Element to animate 33 | * @param className Properties to animate or name of the effect to use. For css animators this represents the className to be added and removed right after the animation is done. 34 | * @param options options for the animation (duration, easing, ...) 35 | * @returns Resolved when the animation is done 36 | */ 37 | animate(element: HTMLElement | Array, className: string): Promise; 38 | 39 | /** 40 | * Run a sequence of animations one after the other. 41 | * @param sequence An array of effectNames or classNames 42 | * @returns Resolved when all animations are done 43 | */ 44 | runSequence(animations: Array): Promise; 45 | 46 | /** 47 | * Execute an 'enter' animation on an element 48 | * @param element Element to animate 49 | * @returns Resolved when the animation is done 50 | */ 51 | enter(element: HTMLElement): Promise; 52 | 53 | /** 54 | * Execute a 'leave' animation on an element 55 | * @param element Element to animate 56 | * @returns Resolved when the animation is done 57 | */ 58 | leave(element: HTMLElement): Promise; 59 | 60 | /** 61 | * Add a class to an element to trigger an animation. 62 | * @param element Element to animate 63 | * @param className Properties to animate or name of the effect to use 64 | * @param suppressEvents Indicates whether or not to suppress animation events. 65 | * @returns Resolved when the animation is done 66 | */ 67 | removeClass(element: HTMLElement, className: string, suppressEvents?: boolean): Promise; 68 | 69 | /** 70 | * Add a class to an element to trigger an animation. 71 | * @param element Element to animate 72 | * @param className Properties to animate or name of the effect to use 73 | * @param suppressEvents Indicates whether or not to suppress animation events. 74 | * @returns Resolved when the animation is done 75 | */ 76 | addClass(element: HTMLElement, className: string, suppressEvents?: boolean): Promise; 77 | } 78 | 79 | /* Public API End */ 80 | /** 81 | * Configuires the CssAnimator as the default animator for Aurelia. 82 | * @param config The FrameworkConfiguration instance. 83 | * @param callback A configuration callback provided by the plugin consumer. 84 | */ 85 | export function configure(config: Object, callback?: ((animator: CssAnimator) => void)): void; 86 | } 87 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-animator-css/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/animator-css/master/typings.json", 5 | "raw": "github:aurelia/animator-css", 6 | "main": "dist/aurelia-animator-css.d.ts", 7 | "global": false, 8 | "name": "aurelia-animator-css", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-binding/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/binding/master/typings.json", 5 | "raw": "github:aurelia/binding", 6 | "main": "dist/aurelia-binding.d.ts", 7 | "global": false, 8 | "name": "aurelia-binding", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-bootstrapper-webpack/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/bootstrapper-webpack/master/dist/aurelia-bootstrapper-webpack.d.ts 3 | declare module 'aurelia-bootstrapper-webpack' { 4 | import 'aurelia-polyfills'; 5 | import { 6 | initialize 7 | } from 'aurelia-pal-browser'; 8 | import { 9 | WebpackLoader 10 | } from 'aurelia-loader-webpack'; 11 | 12 | /** 13 | * Manually bootstraps an application. 14 | * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app. 15 | * @return A Promise that completes when configuration is done. 16 | */ 17 | export function bootstrap(configure: Function): Promise; 18 | } 19 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-bootstrapper-webpack/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/bootstrapper-webpack/master/typings.json", 5 | "raw": "github:aurelia/bootstrapper-webpack", 6 | "main": "dist/aurelia-bootstrapper-webpack.d.ts", 7 | "global": false, 8 | "name": "aurelia-bootstrapper-webpack", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-bootstrapper/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/bootstrapper/master/dist/aurelia-bootstrapper.d.ts 3 | declare module 'aurelia-bootstrapper' { 4 | import 'aurelia-polyfills'; 5 | import { 6 | PLATFORM 7 | } from 'aurelia-pal'; 8 | import { 9 | initialize 10 | } from 'aurelia-pal-browser'; 11 | 12 | /** 13 | * Manually bootstraps an application. 14 | * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app. 15 | * @return A Promise that completes when configuration is done. 16 | */ 17 | export function bootstrap(configure: Function): Promise; 18 | } 19 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-bootstrapper/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/bootstrapper/master/typings.json", 5 | "raw": "github:aurelia/bootstrapper", 6 | "main": "dist/aurelia-bootstrapper.d.ts", 7 | "global": false, 8 | "name": "aurelia-bootstrapper", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-dependency-injection/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/dependency-injection/master/typings.json", 5 | "raw": "github:aurelia/dependency-injection", 6 | "main": "dist/aurelia-dependency-injection.d.ts", 7 | "global": false, 8 | "name": "aurelia-dependency-injection", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-dialog/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/dialog/master/typings.json", 5 | "raw": "github:aurelia/dialog", 6 | "main": "dist/aurelia-dialog.d.ts", 7 | "global": false, 8 | "dependencies": { 9 | "aurelia-dependency-injection": { 10 | "src": "https://raw.githubusercontent.com/aurelia/dependency-injection/master/typings.json", 11 | "raw": "github:aurelia/dependency-injection", 12 | "main": "dist/aurelia-dependency-injection.d.ts", 13 | "global": false, 14 | "name": "aurelia-dependency-injection", 15 | "type": "typings" 16 | }, 17 | "aurelia-metadata": { 18 | "src": "https://raw.githubusercontent.com/aurelia/metadata/master/typings.json", 19 | "raw": "github:aurelia/metadata", 20 | "main": "dist/aurelia-metadata.d.ts", 21 | "global": false, 22 | "dependencies": { 23 | "aurelia-pal": { 24 | "src": "https://raw.githubusercontent.com/aurelia/pal/master/typings.json", 25 | "raw": "github:aurelia/pal", 26 | "main": "dist/aurelia-pal.d.ts", 27 | "global": false, 28 | "name": "aurelia-pal", 29 | "type": "typings" 30 | } 31 | }, 32 | "name": "aurelia-metadata", 33 | "type": "typings" 34 | }, 35 | "aurelia-pal": { 36 | "src": "https://raw.githubusercontent.com/aurelia/pal/master/typings.json", 37 | "raw": "github:aurelia/pal", 38 | "main": "dist/aurelia-pal.d.ts", 39 | "global": false, 40 | "name": "aurelia-pal", 41 | "type": "typings" 42 | }, 43 | "aurelia-templating": { 44 | "src": "https://raw.githubusercontent.com/aurelia/templating/master/typings.json", 45 | "raw": "github:aurelia/templating", 46 | "main": "dist/aurelia-templating.d.ts", 47 | "global": false, 48 | "name": "aurelia-templating", 49 | "type": "typings" 50 | } 51 | }, 52 | "name": "aurelia-dialog", 53 | "type": "typings" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-event-aggregator/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/event-aggregator/master/dist/aurelia-event-aggregator.d.ts 3 | declare module 'aurelia-event-aggregator' { 4 | import * as LogManager from 'aurelia-logging'; 5 | 6 | /** 7 | * Represents a disposable subsciption to an EventAggregator event. 8 | */ 9 | export interface Subscription { 10 | 11 | /** 12 | * Disposes the subscription. 13 | */ 14 | dispose(): void; 15 | } 16 | 17 | /** 18 | * Enables loosely coupled publish/subscribe messaging. 19 | */ 20 | /** 21 | * Enables loosely coupled publish/subscribe messaging. 22 | */ 23 | export class EventAggregator { 24 | 25 | /** 26 | * Creates an instance of the EventAggregator class. 27 | */ 28 | constructor(); 29 | 30 | /** 31 | * Publishes a message. 32 | * @param event The event or channel to publish to. 33 | * @param data The data to publish on the channel. 34 | */ 35 | publish(event: string | any, data?: any): void; 36 | 37 | /** 38 | * Subscribes to a message channel or message type. 39 | * @param event The event channel or event data type. 40 | * @param callback The callback to be invoked when when the specified message is published. 41 | */ 42 | subscribe(event: string | Function, callback: Function): Subscription; 43 | 44 | /** 45 | * Subscribes to a message channel or message type, then disposes the subscription automatically after the first message is received. 46 | * @param event The event channel or event data type. 47 | * @param callback The callback to be invoked when when the specified message is published. 48 | */ 49 | subscribeOnce(event: string | Function, callback: Function): Subscription; 50 | } 51 | 52 | /** 53 | * Includes EA functionality into an object instance. 54 | * @param obj The object to mix Event Aggregator functionality into. 55 | */ 56 | export function includeEventsIn(obj: Object): EventAggregator; 57 | 58 | /** 59 | * Configures a global EA by merging functionality into the Aurelia instance. 60 | * @param config The Aurelia Framework configuration object used to configure the plugin. 61 | */ 62 | export function configure(config: Object): void; 63 | } 64 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-event-aggregator/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/event-aggregator/master/typings.json", 5 | "raw": "github:aurelia/event-aggregator", 6 | "main": "dist/aurelia-event-aggregator.d.ts", 7 | "global": false, 8 | "name": "aurelia-event-aggregator", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-fetch-client/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/fetch-client/master/typings.json", 5 | "raw": "github:aurelia/fetch-client", 6 | "main": "dist/aurelia-fetch-client.d.ts", 7 | "global": false, 8 | "name": "aurelia-fetch-client", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-framework/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/framework/master/typings.json", 5 | "raw": "github:aurelia/framework", 6 | "main": "dist/aurelia-framework.d.ts", 7 | "global": false, 8 | "name": "aurelia-framework", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-history-browser/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/history-browser/master/dist/aurelia-history-browser.d.ts 3 | declare module 'aurelia-history-browser' { 4 | import { 5 | DOM, 6 | PLATFORM 7 | } from 'aurelia-pal'; 8 | import { 9 | History 10 | } from 'aurelia-history'; 11 | 12 | /** 13 | * Provides information about how to handle an anchor event. 14 | */ 15 | export interface AnchorEventInfo { 16 | 17 | /** 18 | * Indicates whether the event should be handled or not. 19 | */ 20 | shouldHandleEvent: boolean; 21 | 22 | /** 23 | * The href of the link or null if not-applicable. 24 | */ 25 | href: string; 26 | 27 | /** 28 | * The anchor element or null if not-applicable. 29 | */ 30 | anchor: Element; 31 | } 32 | 33 | /** 34 | * Class responsible for handling interactions that should trigger browser history navigations. 35 | */ 36 | export class LinkHandler { 37 | 38 | /** 39 | * Activate the instance. 40 | * 41 | * @param history The BrowserHistory instance that navigations should be dispatched to. 42 | */ 43 | activate(history: BrowserHistory): void; 44 | 45 | /** 46 | * Deactivate the instance. Event handlers and other resources should be cleaned up here. 47 | */ 48 | deactivate(): void; 49 | } 50 | 51 | /** 52 | * The default LinkHandler implementation. Navigations are triggered by click events on 53 | * anchor elements with relative hrefs when the history instance is using pushstate. 54 | */ 55 | /** 56 | * The default LinkHandler implementation. Navigations are triggered by click events on 57 | * anchor elements with relative hrefs when the history instance is using pushstate. 58 | */ 59 | export class DefaultLinkHandler extends LinkHandler { 60 | 61 | /** 62 | * Creates an instance of DefaultLinkHandler. 63 | */ 64 | constructor(); 65 | 66 | /** 67 | * Activate the instance. 68 | * 69 | * @param history The BrowserHistory instance that navigations should be dispatched to. 70 | */ 71 | activate(history: BrowserHistory): void; 72 | 73 | /** 74 | * Deactivate the instance. Event handlers and other resources should be cleaned up here. 75 | */ 76 | deactivate(): void; 77 | 78 | /** 79 | * Gets the href and a "should handle" recommendation, given an Event. 80 | * 81 | * @param event The Event to inspect for target anchor and href. 82 | */ 83 | static getEventInfo(event: Event): AnchorEventInfo; 84 | 85 | /** 86 | * Finds the closest ancestor that's an anchor element. 87 | * 88 | * @param el The element to search upward from. 89 | * @returns The link element that is the closest ancestor. 90 | */ 91 | static findClosestAnchor(el: Element): Element; 92 | 93 | /** 94 | * Gets a value indicating whether or not an anchor targets the current window. 95 | * 96 | * @param target The anchor element whose target should be inspected. 97 | * @returns True if the target of the link element is this window; false otherwise. 98 | */ 99 | static targetIsThisWindow(target: Element): boolean; 100 | } 101 | 102 | /** 103 | * Configures the plugin by registering BrowserHistory as the implementation of History in the DI container. 104 | * @param config The FrameworkConfiguration object provided by Aurelia. 105 | */ 106 | export function configure(config: Object): void; 107 | 108 | /** 109 | * An implementation of the basic history API. 110 | */ 111 | export class BrowserHistory extends History { 112 | static inject: any; 113 | 114 | /** 115 | * Creates an instance of BrowserHistory 116 | * @param linkHandler An instance of LinkHandler. 117 | */ 118 | constructor(linkHandler: LinkHandler); 119 | 120 | /** 121 | * Activates the history object. 122 | * @param options The set of options to activate history with. 123 | * @returns Whether or not activation occurred. 124 | */ 125 | activate(options?: Object): boolean; 126 | 127 | /** 128 | * Deactivates the history object. 129 | */ 130 | deactivate(): void; 131 | 132 | /** 133 | * Returns the fully-qualified root of the current history object. 134 | * @returns The absolute root of the application. 135 | */ 136 | getAbsoluteRoot(): string; 137 | 138 | /** 139 | * Causes a history navigation to occur. 140 | * 141 | * @param fragment The history fragment to navigate to. 142 | * @param options The set of options that specify how the navigation should occur. 143 | * @return True if navigation occurred/false otherwise. 144 | */ 145 | navigate(fragment?: string, undefined?: any): boolean; 146 | 147 | /** 148 | * Causes the history state to navigate back. 149 | */ 150 | navigateBack(): void; 151 | 152 | /** 153 | * Sets the document title. 154 | */ 155 | setTitle(title: string): void; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-history-browser/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/history-browser/master/typings.json", 5 | "raw": "github:aurelia/history-browser", 6 | "main": "dist/aurelia-history-browser.d.ts", 7 | "global": false, 8 | "name": "aurelia-history-browser", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-history/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/history/master/dist/aurelia-history.d.ts 3 | declare module 'aurelia-history' { 4 | 5 | /** 6 | * The options that can be specified as part of a history navigation request. 7 | */ 8 | export interface NavigationOptions { 9 | 10 | /** 11 | * Replace the existing route. 12 | */ 13 | replace?: boolean; 14 | 15 | /** 16 | * Trigger the router. 17 | */ 18 | trigger?: boolean; 19 | } 20 | 21 | /** 22 | * An abstract base class for implementors of the basic history api. 23 | */ 24 | export class History { 25 | 26 | /** 27 | * Activates the history object. 28 | * 29 | * @param options The set of options to activate history with. 30 | * @returns Whether or not activation occurred. 31 | */ 32 | activate(options: Object): boolean; 33 | 34 | /** 35 | * Deactivates the history object. 36 | */ 37 | deactivate(): void; 38 | 39 | /** 40 | * Returns the fully-qualified root of the current history object. 41 | * @returns The absolute root of the application. 42 | */ 43 | getAbsoluteRoot(): string; 44 | 45 | /** 46 | * Causes a history navigation to occur. 47 | * 48 | * @param fragment The history fragment to navigate to. 49 | * @param options The set of options that specify how the navigation should occur. 50 | * @returns True if navigation occurred/false otherwise. 51 | */ 52 | navigate(fragment: string, options?: NavigationOptions): boolean; 53 | 54 | /** 55 | * Causes the history state to navigate back. 56 | */ 57 | navigateBack(): void; 58 | 59 | /** 60 | * Updates the title associated with the current location. 61 | */ 62 | setTitle(title: string): void; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-history/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/history/master/typings.json", 5 | "raw": "github:aurelia/history", 6 | "main": "dist/aurelia-history.d.ts", 7 | "global": false, 8 | "name": "aurelia-history", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-loader-webpack/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/loader-webpack/master/dist/aurelia-loader-webpack.d.ts 3 | declare module 'aurelia-loader-webpack' { 4 | import { 5 | Origin 6 | } from 'aurelia-metadata'; 7 | import { 8 | Loader 9 | } from 'aurelia-loader'; 10 | import { 11 | DOM, 12 | PLATFORM 13 | } from 'aurelia-pal'; 14 | 15 | /** 16 | * An implementation of the TemplateLoader interface implemented with text-based loading. 17 | */ 18 | export class TextTemplateLoader { 19 | 20 | /** 21 | * Loads a template. 22 | * @param loader The loader that is requesting the template load. 23 | * @param entry The TemplateRegistryEntry to load and populate with a template. 24 | * @return A promise which resolves when the TemplateRegistryEntry is loaded with a template. 25 | */ 26 | loadTemplate(loader?: any, entry?: any): any; 27 | } 28 | export function ensureOriginOnExports(executed?: any, moduleId?: any): any; 29 | 30 | /** 31 | * A default implementation of the Loader abstraction which works with webpack (extended common-js style). 32 | */ 33 | export class WebpackLoader extends Loader { 34 | constructor(); 35 | 36 | /** 37 | * Maps a module id to a source. 38 | * @param id The module id. 39 | * @param source The source to map the module to. 40 | */ 41 | map(id?: any, source?: any): any; 42 | 43 | /** 44 | * Normalizes a module id. 45 | * @param moduleId The module id to normalize. 46 | * @param relativeTo What the module id should be normalized relative to. 47 | * @return The normalized module id. 48 | */ 49 | normalizeSync(moduleId?: any, relativeTo?: any): any; 50 | 51 | /** 52 | * Normalizes a module id. 53 | * @param moduleId The module id to normalize. 54 | * @param relativeTo What the module id should be normalized relative to. 55 | * @return The normalized module id. 56 | */ 57 | normalize(moduleId?: any, relativeTo?: any): any; 58 | 59 | /** 60 | * Instructs the loader to use a specific TemplateLoader instance for loading templates 61 | * @param templateLoader The instance of TemplateLoader to use for loading templates. 62 | */ 63 | useTemplateLoader(templateLoader?: any): any; 64 | 65 | /** 66 | * Loads a collection of modules. 67 | * @param ids The set of module ids to load. 68 | * @return A Promise for an array of loaded modules. 69 | */ 70 | loadAllModules(ids?: any): any; 71 | 72 | /** 73 | * Loads a module. 74 | * @param id The module id to normalize. 75 | * @return A Promise for the loaded module. 76 | */ 77 | loadModule(id?: any): any; 78 | 79 | /** 80 | * Loads a template. 81 | * @param url The url of the template to load. 82 | * @return A Promise for a TemplateRegistryEntry containing the template. 83 | */ 84 | loadTemplate(url?: any): any; 85 | 86 | /** 87 | * Loads a text-based resource. 88 | * @param url The url of the text file to load. 89 | * @return A Promise for text content. 90 | */ 91 | loadText(url?: any): any; 92 | 93 | /** 94 | * Alters a module id so that it includes a plugin loader. 95 | * @param url The url of the module to load. 96 | * @param pluginName The plugin to apply to the module id. 97 | * @return The plugin-based module id. 98 | */ 99 | applyPluginToUrl(url?: any, pluginName?: any): any; 100 | 101 | /** 102 | * Registers a plugin with the loader. 103 | * @param pluginName The name of the plugin. 104 | * @param implementation The plugin implementation. 105 | */ 106 | addPlugin(pluginName?: any, implementation?: any): any; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-loader-webpack/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/loader-webpack/master/typings.json", 5 | "raw": "github:aurelia/loader-webpack", 6 | "main": "dist/aurelia-loader-webpack.d.ts", 7 | "global": false, 8 | "name": "aurelia-loader-webpack", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-loader/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/loader/master/dist/aurelia-loader.d.ts 3 | declare module 'aurelia-loader' { 4 | import { 5 | relativeToFile 6 | } from 'aurelia-path'; 7 | import { 8 | Origin 9 | } from 'aurelia-metadata'; 10 | 11 | /*eslint no-unused-vars:0*/ 12 | /** 13 | * Represents a plugin to the module loader. 14 | */ 15 | export interface LoaderPlugin { 16 | 17 | /** 18 | * Fetches the resource. 19 | * @param address The address of the resource. 20 | * @return A Promise for the requested resouce. 21 | */ 22 | fetch(address: string): Promise; 23 | } 24 | 25 | /** 26 | * Represents a dependency of a template. 27 | */ 28 | export class TemplateDependency { 29 | 30 | /** 31 | * The source of the dependency. 32 | */ 33 | src: string; 34 | 35 | /** 36 | * The local name of the src when used in the template. 37 | */ 38 | name: string; 39 | 40 | /** 41 | * Creates a template dependency. 42 | * @param src The source of the dependency. 43 | * @param name The local name of the src when used in the template. 44 | */ 45 | constructor(src: string, name?: string); 46 | } 47 | 48 | /** 49 | * Represents an entry in the template registry. 50 | */ 51 | export class TemplateRegistryEntry { 52 | 53 | /** 54 | * The address of the template that this entry represents. 55 | */ 56 | address: string; 57 | 58 | /** 59 | * Indicates whether or not the associated template is loaded . 60 | */ 61 | templateIsLoaded: boolean; 62 | 63 | /** 64 | * Indicates whether the factory is ready to be used to create instances of the associated template. 65 | */ 66 | factoryIsReady: boolean; 67 | 68 | /** 69 | * Sets the resources associated with this entry. 70 | */ 71 | resources: Object; 72 | 73 | /** 74 | * The dependencies of the associated template. Dependencies are not available until after the template is loaded. 75 | */ 76 | dependencies: TemplateDependency[]; 77 | 78 | /** 79 | * Creates an instance of TemplateRegistryEntry. 80 | * @param address The address of the template that this entry represents. 81 | */ 82 | constructor(address: string); 83 | 84 | /** 85 | * Gets the template for this registry entry. 86 | */ 87 | template: Element; 88 | 89 | /** 90 | * Gets the factory capable of creating instances of this template. 91 | */ 92 | factory: any; 93 | 94 | /** 95 | * Adds a dependency to this template registry entry. Cannot be called until after the template is set. 96 | * @param src The dependency instance or a relative path to its module. 97 | * @param name An optional local name by which this dependency is used in the template. 98 | */ 99 | addDependency(src: string | Function, name?: string): void; 100 | } 101 | 102 | /** 103 | * A generic resource loader, for loading modules, html, css and more. 104 | */ 105 | /** 106 | * A generic resource loader, for loading modules, html, css and more. 107 | */ 108 | export class Loader { 109 | 110 | /** 111 | * Creates an instance of Loader. 112 | */ 113 | constructor(); 114 | 115 | /** 116 | * Maps a module id to a source. 117 | * @param id The module id. 118 | * @param source The source to map the module to. 119 | */ 120 | map(id: string, source: string): void; 121 | 122 | /** 123 | * Normalizes a module id. 124 | * @param moduleId The module id to normalize. 125 | * @param relativeTo What the module id should be normalized relative to. 126 | * @return The normalized module id. 127 | */ 128 | normalizeSync(moduleId: string, relativeTo: string): string; 129 | 130 | /** 131 | * Normalizes a module id. 132 | * @param moduleId The module id to normalize. 133 | * @param relativeTo What the module id should be normalized relative to. 134 | * @return A promise for the normalized module id. 135 | */ 136 | normalize(moduleId: string, relativeTo: string): Promise; 137 | 138 | /** 139 | * Loads a module. 140 | * @param id The module id to normalize. 141 | * @return A Promise for the loaded module. 142 | */ 143 | loadModule(id: string): Promise; 144 | 145 | /** 146 | * Loads a collection of modules. 147 | * @param ids The set of module ids to load. 148 | * @return A Promise for an array of loaded modules. 149 | */ 150 | loadAllModules(ids: string[]): Promise; 151 | 152 | /** 153 | * Loads a template. 154 | * @param url The url of the template to load. 155 | * @return A Promise for a TemplateRegistryEntry containing the template. 156 | */ 157 | loadTemplate(url: string): Promise; 158 | 159 | /** 160 | * Loads a text-based resource. 161 | * @param url The url of the text file to load. 162 | * @return A Promise for text content. 163 | */ 164 | loadText(url: string): Promise; 165 | 166 | /** 167 | * Alters a module id so that it includes a plugin loader. 168 | * @param url The url of the module to load. 169 | * @param pluginName The plugin to apply to the module id. 170 | * @return The plugin-based module id. 171 | */ 172 | applyPluginToUrl(url: string, pluginName: string): string; 173 | 174 | /** 175 | * Registers a plugin with the loader. 176 | * @param pluginName The name of the plugin. 177 | * @param implementation The plugin implementation. 178 | */ 179 | addPlugin(pluginName: string, implementation: LoaderPlugin): void; 180 | 181 | /** 182 | * Gets or creates a TemplateRegistryEntry for the provided address. 183 | * @param address The address of the template. 184 | * @return The located or created TemplateRegistryEntry. 185 | */ 186 | getOrCreateTemplateRegistryEntry(address: string): TemplateRegistryEntry; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-loader/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/loader/master/typings.json", 5 | "raw": "github:aurelia/loader", 6 | "main": "dist/aurelia-loader.d.ts", 7 | "global": false, 8 | "name": "aurelia-loader", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-logging-console/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/logging-console/master/dist/aurelia-logging-console.d.ts 3 | declare module 'aurelia-logging-console' { 4 | import { 5 | Logger 6 | } from 'aurelia-logging'; 7 | 8 | /* 9 | * An implementation of the Appender interface. 10 | */ 11 | export class ConsoleAppender { 12 | 13 | /** 14 | * Appends a debug log. 15 | * 16 | * @param logger The source logger. 17 | * @param rest The data to log. 18 | */ 19 | debug(logger: Logger, ...rest: any[]): void; 20 | 21 | /** 22 | * Appends an info log. 23 | * 24 | * @param logger The source logger. 25 | * @param rest The data to log. 26 | */ 27 | info(logger: Logger, ...rest: any[]): void; 28 | 29 | /** 30 | * Appends a warning log. 31 | * 32 | * @param logger The source logger. 33 | * @param rest The data to log. 34 | */ 35 | warn(logger: Logger, ...rest: any[]): void; 36 | 37 | /** 38 | * Appends an error log. 39 | * 40 | * @param logger The source logger. 41 | * @param rest The data to log. 42 | */ 43 | error(logger: Logger, ...rest: any[]): void; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-logging-console/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/logging-console/master/typings.json", 5 | "raw": "github:aurelia/logging-console", 6 | "main": "dist/aurelia-logging-console.d.ts", 7 | "global": false, 8 | "name": "aurelia-logging-console", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-logging/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/logging/master/dist/aurelia-logging.d.ts 3 | declare module 'aurelia-logging' { 4 | 5 | /** 6 | * Specifies the available logging levels. 7 | */ 8 | export interface LogLevel { 9 | 10 | /** 11 | * No logging. 12 | */ 13 | none: number; 14 | 15 | /** 16 | * Log only error messages. 17 | */ 18 | error: number; 19 | 20 | /** 21 | * Log warnings messages or above. 22 | */ 23 | warn: number; 24 | 25 | /** 26 | * Log informational messages or above. 27 | */ 28 | info: number; 29 | 30 | /** 31 | * Log all messages. 32 | */ 33 | debug: number; 34 | } 35 | 36 | /** 37 | * Implemented by classes which wish to append log data to a target data store. 38 | */ 39 | export interface Appender { 40 | 41 | /** 42 | * Appends a debug log. 43 | * 44 | * @param logger The source logger. 45 | * @param rest The data to log. 46 | */ 47 | debug(logger: Logger, ...rest: any[]): void; 48 | 49 | /** 50 | * Appends an info log. 51 | * 52 | * @param logger The source logger. 53 | * @param rest The data to log. 54 | */ 55 | info(logger: Logger, ...rest: any[]): void; 56 | 57 | /** 58 | * Appends a warning log. 59 | * 60 | * @param logger The source logger. 61 | * @param rest The data to log. 62 | */ 63 | warn(logger: Logger, ...rest: any[]): void; 64 | 65 | /** 66 | * Appends an error log. 67 | * 68 | * @param logger The source logger. 69 | * @param rest The data to log. 70 | */ 71 | error(logger: Logger, ...rest: any[]): void; 72 | } 73 | 74 | /** 75 | * Specifies the available logging levels. 76 | */ 77 | /** 78 | * Specifies the available logging levels. 79 | */ 80 | export const logLevel: LogLevel; 81 | 82 | /** 83 | * Gets the instance of a logger associated with a particular id (or creates one if it doesn't already exist). 84 | * 85 | * @param id The id of the logger you wish to get an instance of. 86 | * @return The instance of the logger, or creates a new logger if none exists for that id. 87 | */ 88 | export function getLogger(id: string): Logger; 89 | 90 | /** 91 | * Adds an appender capable of processing logs and channeling them to an output. 92 | * 93 | * @param appender An appender instance to begin processing logs with. 94 | */ 95 | /** 96 | * Adds an appender capable of processing logs and channeling them to an output. 97 | * 98 | * @param appender An appender instance to begin processing logs with. 99 | */ 100 | export function addAppender(appender: Appender): void; 101 | 102 | /** 103 | * Sets the level of logging for the application loggers. 104 | * 105 | * @param level Matches a value of logLevel specifying the level of logging. 106 | */ 107 | export function setLevel(level: number): void; 108 | 109 | /** 110 | * A logger logs messages to a set of appenders, depending on the log level that is set. 111 | */ 112 | export class Logger { 113 | 114 | /** 115 | * The id that the logger was created with. 116 | */ 117 | id: string; 118 | 119 | /** 120 | * You cannot instantiate the logger directly - you must use the getLogger method instead. 121 | */ 122 | constructor(id: string, key: Object); 123 | 124 | /** 125 | * Logs a debug message. 126 | * 127 | * @param message The message to log. 128 | * @param rest The data to log. 129 | */ 130 | debug(message: string, ...rest: any[]): void; 131 | 132 | /** 133 | * Logs info. 134 | * 135 | * @param message The message to log. 136 | * @param rest The data to log. 137 | */ 138 | info(message: string, ...rest: any[]): void; 139 | 140 | /** 141 | * Logs a warning. 142 | * 143 | * @param message The message to log. 144 | * @param rest The data to log. 145 | */ 146 | warn(message: string, ...rest: any[]): void; 147 | 148 | /** 149 | * Logs an error. 150 | * 151 | * @param message The message to log. 152 | * @param rest The data to log. 153 | */ 154 | error(message: string, ...rest: any[]): void; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-logging/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/logging/master/typings.json", 5 | "raw": "github:aurelia/logging", 6 | "main": "dist/aurelia-logging.d.ts", 7 | "global": false, 8 | "name": "aurelia-logging", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-metadata/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/metadata/master/typings.json", 5 | "raw": "github:aurelia/metadata", 6 | "main": "dist/aurelia-metadata.d.ts", 7 | "global": false, 8 | "dependencies": { 9 | "aurelia-pal": { 10 | "src": "https://raw.githubusercontent.com/aurelia/pal/master/typings.json", 11 | "raw": "github:aurelia/pal", 12 | "main": "dist/aurelia-pal.d.ts", 13 | "global": false, 14 | "name": "aurelia-pal", 15 | "type": "typings" 16 | } 17 | }, 18 | "name": "aurelia-metadata", 19 | "type": "typings" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-pal-browser/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/pal-browser/master/typings.json", 5 | "raw": "github:aurelia/pal-browser", 6 | "main": "dist/aurelia-pal-browser.d.ts", 7 | "global": false, 8 | "dependencies": { 9 | "aurelia-pal": { 10 | "src": "https://raw.githubusercontent.com/aurelia/pal/master/typings.json", 11 | "raw": "github:aurelia/pal", 12 | "main": "dist/aurelia-pal.d.ts", 13 | "global": false, 14 | "name": "aurelia-pal", 15 | "type": "typings" 16 | } 17 | }, 18 | "name": "aurelia-pal-browser", 19 | "type": "typings" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-pal/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/pal/master/typings.json", 5 | "raw": "github:aurelia/pal", 6 | "main": "dist/aurelia-pal.d.ts", 7 | "global": false, 8 | "name": "aurelia-pal", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-path/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/path/master/dist/aurelia-path.d.ts 3 | declare module 'aurelia-path' { 4 | 5 | /** 6 | * Calculates a path relative to a file. 7 | * 8 | * @param name The relative path. 9 | * @param file The file path. 10 | * @return The calculated path. 11 | */ 12 | export function relativeToFile(name: string, file: string): string; 13 | 14 | /** 15 | * Joins two paths. 16 | * 17 | * @param path1 The first path. 18 | * @param path2 The second path. 19 | * @return The joined path. 20 | */ 21 | export function join(path1: string, path2: string): string; 22 | 23 | /** 24 | * Generate a query string from an object. 25 | * 26 | * @param params Object containing the keys and values to be used. 27 | * @returns The generated query string, excluding leading '?'. 28 | */ 29 | export function buildQueryString(params: Object): string; 30 | 31 | /** 32 | * Parse a query string. 33 | * 34 | * @param queryString The query string to parse. 35 | * @returns Object with keys and values mapped from the query string. 36 | */ 37 | export function parseQueryString(queryString: string): Object; 38 | } 39 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-path/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/path/master/typings.json", 5 | "raw": "github:aurelia/path", 6 | "main": "dist/aurelia-path.d.ts", 7 | "global": false, 8 | "name": "aurelia-path", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-polyfills/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/polyfills/master/dist/aurelia-polyfills.d.ts 3 | declare module 'aurelia-polyfills' { 4 | import { 5 | PLATFORM 6 | } from 'aurelia-pal'; 7 | } 8 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-polyfills/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/polyfills/master/typings.json", 5 | "raw": "github:aurelia/polyfills", 6 | "main": "dist/aurelia-polyfills.d.ts", 7 | "global": false, 8 | "name": "aurelia-polyfills", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-route-recognizer/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/route-recognizer/master/dist/aurelia-route-recognizer.d.ts 3 | declare module 'aurelia-route-recognizer' { 4 | import { 5 | buildQueryString, 6 | parseQueryString 7 | } from 'aurelia-path'; 8 | export interface RouteHandler { 9 | name: string; 10 | } 11 | export interface ConfigurableRoute { 12 | path: string; 13 | handler: RouteHandler; 14 | caseSensitive: boolean; 15 | } 16 | export interface HandlerEntry { 17 | handler: RouteHandler; 18 | names: string[]; 19 | } 20 | export interface RecognizedRoute { 21 | handler: RouteHandler; 22 | params: Object; 23 | isDynamic: boolean; 24 | } 25 | export interface CharSpec { 26 | invalidChars?: string; 27 | validChars?: string; 28 | repeat?: boolean; 29 | } 30 | 31 | // A State has a character specification and (`charSpec`) and a list of possible 32 | // subsequent states (`nextStates`). 33 | // 34 | // If a State is an accepting state, it will also have several additional 35 | // properties: 36 | // 37 | // * `regex`: A regular expression that is used to extract parameters from paths 38 | // that reached this accepting state. 39 | // * `handlers`: Information on how to convert the list of captures into calls 40 | // to registered handlers with the specified parameters. 41 | // * `types`: How many static, dynamic, or star segments in this route. Used to 42 | // decide which route to use if multiple registered routes match a path. 43 | // 44 | // Currently, State is implemented naively by looping over `nextStates` and 45 | // comparing a character specification against a character. A more efficient 46 | // implementation would use a hash of keys pointing at one or more next states. 47 | export class State { 48 | constructor(charSpec: CharSpec); 49 | get(charSpec: CharSpec): State; 50 | put(charSpec: CharSpec): State; 51 | 52 | // Find a list of child states matching the next character 53 | match(ch: string): State[]; 54 | } 55 | 56 | // A Segment represents a segment in the original route description. 57 | // Each Segment type provides an `eachChar` and `regex` method. 58 | // 59 | // The `eachChar` method invokes the callback with one or more character 60 | // specifications. A character specification consumes one or more input 61 | // characters. 62 | // 63 | // The `regex` method returns a regex fragment for the segment. If the 64 | // segment is a dynamic or star segment, the regex fragment also includes 65 | // a capture. 66 | // 67 | // A character specification contains: 68 | // 69 | // * `validChars`: a String with a list of all valid characters, or 70 | // * `invalidChars`: a String with a list of all invalid characters 71 | // * `repeat`: true if the character specification can repeat 72 | export class StaticSegment { 73 | constructor(string: string, caseSensitive: boolean); 74 | eachChar(callback: ((spec: CharSpec) => void)): void; 75 | regex(): string; 76 | generate(): string; 77 | } 78 | export class DynamicSegment { 79 | constructor(name: string); 80 | eachChar(callback: ((spec: CharSpec) => void)): void; 81 | regex(): string; 82 | generate(params: Object, consumed: Object): string; 83 | } 84 | export class StarSegment { 85 | constructor(name: string); 86 | eachChar(callback: ((spec: CharSpec) => void)): void; 87 | regex(): string; 88 | generate(params: Object, consumed: Object): string; 89 | } 90 | export class EpsilonSegment { 91 | eachChar(): void; 92 | regex(): string; 93 | generate(): string; 94 | } 95 | 96 | /** 97 | * Class that parses route patterns and matches path strings. 98 | * 99 | * @class RouteRecognizer 100 | * @constructor 101 | */ 102 | /** 103 | * Class that parses route patterns and matches path strings. 104 | * 105 | * @class RouteRecognizer 106 | * @constructor 107 | */ 108 | export class RouteRecognizer { 109 | constructor(); 110 | 111 | /** 112 | * Parse a route pattern and add it to the collection of recognized routes. 113 | * 114 | * @param route The route to add. 115 | */ 116 | add(route: ConfigurableRoute | ConfigurableRoute[]): State; 117 | 118 | /** 119 | * Retrieve the handlers registered for the named route. 120 | * 121 | * @param name The name of the route. 122 | * @returns The handlers. 123 | */ 124 | handlersFor(name: string): HandlerEntry[]; 125 | 126 | /** 127 | * Check if this RouteRecognizer recognizes a named route. 128 | * 129 | * @param name The name of the route. 130 | * @returns True if the named route is recognized. 131 | */ 132 | hasRoute(name: string): boolean; 133 | 134 | /** 135 | * Generate a path and query string from a route name and params object. 136 | * 137 | * @param name The name of the route. 138 | * @param params The route params to use when populating the pattern. 139 | * Properties not required by the pattern will be appended to the query string. 140 | * @returns The generated absolute path and query string. 141 | */ 142 | generate(name: string, params: Object): string; 143 | 144 | /** 145 | * Match a path string against registered route patterns. 146 | * 147 | * @param path The path to attempt to match. 148 | * @returns Array of objects containing `handler`, `params`, and 149 | * `isDynanic` values for the matched route(s), or undefined if no match 150 | * was found. 151 | */ 152 | recognize(path: string): RecognizedRoute[]; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-route-recognizer/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/route-recognizer/master/typings.json", 5 | "raw": "github:aurelia/route-recognizer", 6 | "main": "dist/aurelia-route-recognizer.d.ts", 7 | "global": false, 8 | "name": "aurelia-route-recognizer", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-router/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/router/master/typings.json", 5 | "raw": "github:aurelia/router", 6 | "main": "dist/aurelia-router.d.ts", 7 | "global": false, 8 | "name": "aurelia-router", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-task-queue/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/task-queue/master/dist/aurelia-task-queue.d.ts 3 | declare module 'aurelia-task-queue' { 4 | import { 5 | DOM, 6 | FEATURE 7 | } from 'aurelia-pal'; 8 | 9 | /** 10 | * Either a Function or a class with a call method that will do work when dequeued. 11 | */ 12 | export interface Task { 13 | 14 | /** 15 | * Call it. 16 | */ 17 | call(): void; 18 | } 19 | 20 | /** 21 | * Implements an asynchronous task queue. 22 | */ 23 | /** 24 | * Implements an asynchronous task queue. 25 | */ 26 | export class TaskQueue { 27 | 28 | /** 29 | * Creates an instance of TaskQueue. 30 | */ 31 | constructor(); 32 | 33 | /** 34 | * Queues a task on the micro task queue for ASAP execution. 35 | * @param task The task to queue up for ASAP execution. 36 | */ 37 | queueMicroTask(task: Task | Function): void; 38 | 39 | /** 40 | * Queues a task on the macro task queue for turn-based execution. 41 | * @param task The task to queue up for turn-based execution. 42 | */ 43 | queueTask(task: Task | Function): void; 44 | 45 | /** 46 | * Immediately flushes the task queue. 47 | */ 48 | flushTaskQueue(): void; 49 | 50 | /** 51 | * Immediately flushes the micro task queue. 52 | */ 53 | flushMicroTaskQueue(): void; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-task-queue/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/task-queue/master/typings.json", 5 | "raw": "github:aurelia/task-queue", 6 | "main": "dist/aurelia-task-queue.d.ts", 7 | "global": false, 8 | "name": "aurelia-task-queue", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating-binding/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/templating-binding/master/dist/aurelia-templating-binding.d.ts 3 | declare module 'aurelia-templating-binding' { 4 | import * as LogManager from 'aurelia-logging'; 5 | import { 6 | camelCase, 7 | SVGAnalyzer, 8 | bindingMode, 9 | connectable, 10 | enqueueBindingConnect, 11 | Parser, 12 | ObserverLocator, 13 | EventManager, 14 | ListenerExpression, 15 | BindingExpression, 16 | CallExpression, 17 | NameExpression 18 | } from 'aurelia-binding'; 19 | import { 20 | BehaviorInstruction, 21 | BindingLanguage 22 | } from 'aurelia-templating'; 23 | export class AttributeMap { 24 | static inject: any; 25 | elements: any; 26 | allElements: any; 27 | constructor(svg?: any); 28 | 29 | /** 30 | * Maps a specific HTML element attribute to a javascript property. 31 | */ 32 | register(elementName?: any, attributeName?: any, propertyName?: any): any; 33 | 34 | /** 35 | * Maps an HTML attribute to a javascript property. 36 | */ 37 | registerUniversal(attributeName?: any, propertyName?: any): any; 38 | 39 | /** 40 | * Returns the javascript property name for a particlar HTML attribute. 41 | */ 42 | map(elementName?: any, attributeName?: any): any; 43 | } 44 | export class InterpolationBindingExpression { 45 | constructor(observerLocator?: any, targetProperty?: any, parts?: any, mode?: any, lookupFunctions?: any, attribute?: any); 46 | createBinding(target?: any): any; 47 | } 48 | export class InterpolationBinding { 49 | constructor(observerLocator?: any, parts?: any, target?: any, targetProperty?: any, mode?: any, lookupFunctions?: any); 50 | interpolate(): any; 51 | updateOneTimeBindings(): any; 52 | bind(source?: any): any; 53 | unbind(): any; 54 | } 55 | export class ChildInterpolationBinding { 56 | constructor(target?: any, observerLocator?: any, sourceExpression?: any, mode?: any, lookupFunctions?: any, targetProperty?: any, left?: any, right?: any); 57 | updateTarget(value?: any): any; 58 | call(): any; 59 | bind(source?: any): any; 60 | unbind(): any; 61 | connect(evaluate?: any): any; 62 | } 63 | 64 | /*eslint dot-notation:0*/ 65 | export class SyntaxInterpreter { 66 | static inject: any; 67 | constructor(parser?: any, observerLocator?: any, eventManager?: any, attributeMap?: any); 68 | interpret(resources?: any, element?: any, info?: any, existingInstruction?: any, context?: any): any; 69 | handleUnknownCommand(resources?: any, element?: any, info?: any, existingInstruction?: any, context?: any): any; 70 | determineDefaultBindingMode(element?: any, attrName?: any, context?: any): any; 71 | bind(resources?: any, element?: any, info?: any, existingInstruction?: any, context?: any): any; 72 | trigger(resources?: any, element?: any, info?: any): any; 73 | delegate(resources?: any, element?: any, info?: any): any; 74 | call(resources?: any, element?: any, info?: any, existingInstruction?: any): any; 75 | options(resources?: any, element?: any, info?: any, existingInstruction?: any, context?: any): any; 76 | 'for'(resources?: any, element?: any, info?: any, existingInstruction?: any): any; 77 | 'two-way'(resources?: any, element?: any, info?: any, existingInstruction?: any): any; 78 | 'one-way'(resources?: any, element?: any, info?: any, existingInstruction?: any): any; 79 | 'one-time'(resources?: any, element?: any, info?: any, existingInstruction?: any): any; 80 | } 81 | export class TemplatingBindingLanguage extends BindingLanguage { 82 | static inject: any; 83 | constructor(parser?: any, observerLocator?: any, syntaxInterpreter?: any, attributeMap?: any); 84 | inspectAttribute(resources?: any, elementName?: any, attrName?: any, attrValue?: any): any; 85 | createAttributeInstruction(resources?: any, element?: any, theInfo?: any, existingInstruction?: any, context?: any): any; 86 | inspectTextContent(resources?: any, value?: any): any; 87 | parseInterpolation(resources?: any, value?: any): any; 88 | } 89 | export function configure(config?: any): any; 90 | } 91 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating-binding/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/templating-binding/master/typings.json", 5 | "raw": "github:aurelia/templating-binding", 6 | "main": "dist/aurelia-templating-binding.d.ts", 7 | "global": false, 8 | "name": "aurelia-templating-binding", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating-resources/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/templating-resources/master/typings.json", 5 | "raw": "github:aurelia/templating-resources", 6 | "main": "dist/aurelia-templating-resources.d.ts", 7 | "global": false, 8 | "name": "aurelia-templating-resources", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating-router/index.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by typings 2 | // Source: https://raw.githubusercontent.com/aurelia/templating-router/master/dist/aurelia-templating-router.d.ts 3 | declare module 'aurelia-templating-router' { 4 | import * as LogManager from 'aurelia-logging'; 5 | import { 6 | customAttribute, 7 | bindable, 8 | ViewSlot, 9 | ViewLocator, 10 | customElement, 11 | noView, 12 | BehaviorInstruction, 13 | CompositionTransaction, 14 | CompositionEngine, 15 | ShadowDOM 16 | } from 'aurelia-templating'; 17 | import { 18 | inject, 19 | Container 20 | } from 'aurelia-dependency-injection'; 21 | import { 22 | Router, 23 | RouteLoader 24 | } from 'aurelia-router'; 25 | import { 26 | DOM 27 | } from 'aurelia-pal'; 28 | import { 29 | Origin 30 | } from 'aurelia-metadata'; 31 | import { 32 | relativeToFile 33 | } from 'aurelia-path'; 34 | export class RouteHref { 35 | constructor(router?: any, element?: any); 36 | bind(): any; 37 | unbind(): any; 38 | attributeChanged(value?: any, previous?: any): any; 39 | processChange(): any; 40 | } 41 | export class RouterView { 42 | swapOrder: any; 43 | layoutView: any; 44 | layoutViewModel: any; 45 | layoutModel: any; 46 | constructor(element?: any, container?: any, viewSlot?: any, router?: any, viewLocator?: any, compositionTransaction?: any, compositionEngine?: any); 47 | created(owningView?: any): any; 48 | bind(bindingContext?: any, overrideContext?: any): any; 49 | process(viewPortInstruction?: any, waitToSwap?: any): any; 50 | swap(viewPortInstruction?: any): any; 51 | } 52 | export class TemplatingRouteLoader extends RouteLoader { 53 | constructor(compositionEngine?: any); 54 | loadRoute(router?: any, config?: any): any; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating-router/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/templating-router/master/typings.json", 5 | "raw": "github:aurelia/templating-router", 6 | "main": "dist/aurelia-templating-router.d.ts", 7 | "global": false, 8 | "name": "aurelia-templating-router", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/typings/modules/aurelia-templating/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "resolution": "main", 3 | "tree": { 4 | "src": "https://raw.githubusercontent.com/aurelia/templating/master/typings.json", 5 | "raw": "github:aurelia/templating", 6 | "main": "dist/aurelia-templating.d.ts", 7 | "global": false, 8 | "name": "aurelia-templating", 9 | "type": "typings" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /exar-ui/wallaby.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (wallaby) { 3 | 4 | return { 5 | files: [ 6 | 7 | {pattern: 'jspm_packages/system.js', instrument: false}, 8 | {pattern: 'config.js', instrument: false}, 9 | 10 | {pattern: 'src/**/*.ts', load: false} 11 | 12 | ], 13 | 14 | tests: [ 15 | {pattern: 'test/unit/**/*.spec.ts', load: false} 16 | ], 17 | 18 | 19 | middleware: (app, express) => { 20 | app.use('/jspm_packages', express.static(require('path').join(__dirname, 'jspm_packages'))); 21 | }, 22 | 23 | bootstrap: function (wallaby) { 24 | wallaby.delayStart(); 25 | 26 | System.config({ 27 | paths: { 28 | "*": null, 29 | "src/*": "src/*", 30 | "typescript": "node_modules/typescript/lib/typescript.js", 31 | "systemjs": "node_modules/systemjs/dist/system.js", 32 | 'system-polyfills': 'node_modules/systemjs/dist/system-polyfills.js', 33 | 'es6-module-loader': 'node_modules/es6-module-loader/dist/es6-module-loader.js' 34 | }, 35 | packages: { 36 | 'test/unit': { 37 | defaultExtension: 'ts' 38 | }, 39 | 'src': { 40 | defaultExtension: 'ts' 41 | } 42 | }, 43 | transpiler: 'typescript' 44 | }); 45 | 46 | var promises = []; 47 | for (var i = 0, len = wallaby.tests.length; i < len; i++) { 48 | promises.push(System['import'](wallaby.tests[i].replace(/\.js$/, ''))); 49 | } 50 | 51 | Promise.all(promises).then(function () { 52 | wallaby.start(); 53 | }); 54 | }, 55 | 56 | debug: false 57 | }; 58 | }; 59 | -------------------------------------------------------------------------------- /test-all: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./exar-core && cargo test && cargo test --features serde-serialization --no-default-features && 4 | cd ../exar-net && cargo test && 5 | cd ../exar-server && cargo test && cargo test --features serde-serialization --no-default-features && 6 | cd ../exar-client && cargo test && 7 | cd ../exar-testkit && cargo test && 8 | cd ../exar-db && cargo test && 9 | cd .. 10 | --------------------------------------------------------------------------------