{"id":15561174,"url":"https://github.com/sneels/parkds","last_synced_at":"2026-02-24T09:33:12.020Z","repository":{"id":57319203,"uuid":"199641894","full_name":"sneels/ParkDS","owner":"sneels","description":"Connect all your Data Sources via 1 process (Cross-Domain + Single-Domain)","archived":false,"fork":false,"pushed_at":"2019-10-15T19:09:54.000Z","size":36891,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-23T21:48:36.954Z","etag":null,"topics":["cross-domain","data","database","datasource","datasources","javascript","source"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sneels.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-30T11:51:32.000Z","updated_at":"2019-10-15T19:09:56.000Z","dependencies_parsed_at":"2022-08-25T20:41:14.553Z","dependency_job_id":null,"html_url":"https://github.com/sneels/ParkDS","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sneels%2FParkDS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sneels%2FParkDS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sneels%2FParkDS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sneels%2FParkDS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sneels","download_url":"https://codeload.github.com/sneels/ParkDS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250522294,"owners_count":21444510,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cross-domain","data","database","datasource","datasources","javascript","source"],"created_at":"2024-10-02T16:05:53.086Z","updated_at":"2025-11-06T19:01:32.624Z","avatar_url":"https://github.com/sneels.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# ParkDS: A Cross-Domain Data Source Collection\n ParkDS is a simple library that allows for several Data sources *(databases, file servers, etc.)* to be able to connect to eachother in a safe and quick way by using SSL WebSocket connections between each domain.\n\n# Table of Contents\n* [Installation](#installation)\n* [Usage](#usage)\n  * [Setting Up ParkDS](#usageSetup)\n   * [Single-Domain](#usageSetupSingle)\n   * [Cross-Domain](#usageSetupMulti)\n   * [Preview Connectors](#usagePreviewConnectors)\n     * [MS SQL](#usagePreviewConnectorsMssql)\n     * [My SQL](#usagePreviewConnectorsMysql)\n   * [Logging](#logging)\n     * [Logging Observer](#loggingObserver)\n* License\n\u003ca name=\"installation\"/\u003e\n\n# Installation\n`npm install parkds`\n\n\u003ca name=\"usage\"/\u003e\n\n# Usage\n\n\u003ca name=\"usageSetup\"/\u003e\n\n## Setting Up ParkDS\n\nFirst off, you'll need to build your ParkDS Config, how you store that config is up to you. In the example it will be hard coded, but you could easily work with a JSON file or something else.\n***NOTE: When a domain is considered cloud it means it can act as a websocket server, if not, it can only make a websocket client connection to a server, you require a 'cloud' domain if you are working cross domain***\n\n\u003ca name=\"usageSetupSingle\"/\u003e\n\n### Setting up the config for Single-Domain\n**The configuration:**\n```javascript\nconst fs = require('fs');\nconst ParkDS = require('parkds');\nconst Config = ParkDS.Config.Instance;\n\n// We need to add at least one domain for ParkDS to work, so do just that.\nconst domain = new ParkDS.Config.Domain();\ndomain.Name = \"Local\";\ndomain.Path = \"local.example.org\";\ndomain.Port = 443;\n// Important if you are working in single-domain, setting this to true will start an arbitrary websocket server.\ndomain.IsCloud = false; \ndomain.Account.User = \"user\";\ndomain.Account.Passworkd = \"pass\";\n\nConfig.Domains.Add(domain);\n\n// Set the current domain\n// And Admin Account is required upon creation, but is as of yet not implemented\nConfig.Settings.Set(domain, domain.Account, domain.Account); \n\n// Add Certificates\nConfig.Certificate.Cert = fs.readFileSync('./cert.pem');\nConfig.Certificate.Key = fs.readFileSync('./key.pem');\n\n// Let's add a MySql database\nconst datasource = new ParkDS.Config.DataSource();\ndatasource.Name = \"MyMySqlDB\";\ndatasource.Type = \"MySql\";\ndatasource.Domain = \"Local\" // this has to be the name of the domain we added to the config\ndatasource.Path = \"path.to.mysql.database\";\ndatasource.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource);\n\nconst datasource2 = new ParkDS.Config.DataSource();\ndatasource2.Name = \"MsSqlDatabase\";\ndatasource2.Type = \"mssql\";\ndatasource2.Domain = \"Local\" // this has to be the name of the domain we added to the config\ndatasource2.Path = \"path.to.mssql.database\";\ndatasource2.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource2);\n\n// Set up the Data Source Connectors\n// MyMySqlDB\nconst myMysqlConnector = require('./myMysqlConnector');\nconst mysqlconn = new myMysqlConnector(ParkDS.Config.Instance.GetDataSourceByName('MyMySqlDB'));\nParkDS.Connector.Instance.Add(mysqlconn);\n\n//MsSqlDatabase\nconst myMssqlConnector = require('./myMssqlConnector');\nconst mssqlconn = new myMssqlConnector(ParkDS.Config.Instance.GetDataSourceByName('MsSqlDatabase');\nParkDS.Connector.Instance.Add(mssqlconn);\n\n// Start ParkDS\nconst pds = new ParkDS();\npds.Start();\n// Connectors are connected, if websocket connections are required, they are started, if need be, the websocket server is running.\n```\n\u003ca name=\"usageSetupMulti\"/\u003e\n\n### Setting up the config for Cross-Domain\n\n```javascript\nconst ParkDS = require('parkds');\nconst Config = ParkDS.Config.Instance;\n// Let's add a cloud domain\nlet domain = new ParkDS.Config.Domain();\ndomain.Name = \"Cloud1\";\ndomain.Path = \"cloud1.example.org\";\ndomain.Port = 443;\ndomain.IsCloud = true;\ndomain.Account.User = \"user\";\ndomain.Account.Passworkd = \"pass\";\n\n// Add the domain to the Config list\nConfig.Domains.Add(domain);\n\n// Let's add another cloud domain\ndomain = new ParkDS.Config.Domain();\ndomain.Name = \"Cloud2\";\ndomain.Path = \"cloud2.example.org\";\ndomain.Port = 44300;\ndomain.IsCloud = true;\ndomain.Account.User = \"user\";\ndomain.Account.Passworkd = \"pass\";\n\n// Let's add an on-premise domain\ndomain = new ParkDS.Config.Domain();\ndomain.Name = \"Premise1\";\ndomain.Path = \"premise1.example.org\";\ndomain.Port = 8080;\ndomain.IsCloud = false;\ndomain.Account.User = \"user\";\ndomain.Account.Passworkd = \"pass\";\n// Add the domain to the Config list\nConfig.Domains.Add(domain);\n\n// Let's add an on-premise domain\ndomain = new ParkDS.Config.Domain();\ndomain.Name = \"Premise1\";\ndomain.Path = \"premise1.example.org\";\ndomain.Port = 8000;\ndomain.IsCloud = false;\ndomain.Account.User = \"user\";\ndomain.Account.Passworkd = \"pass\";\n// Add the domain to the Config list\nConfig.Domains.Add(domain);\n\n// Set the current domain\n// And Admin Account is required upon creation, but is as of yet not implemented\nConfig.Settings.Set(Config.Domains.GetDomainByName('Premise1'), Config.Domains.GetDomainByName('Premise1').Account, Config.Domains.GetDomainByName('Premise1').Account); \n\n// Add Certificates\nConfig.Certificate.Cert = fs.readFileSync('./cert.pem');\nConfig.Certificate.Key = fs.readFileSync('./key.pem');\n\n// Let's add a MySql database\nconst datasource = new ParkDS.Config.DataSource();\ndatasource.Name = \"MyMySqlDB\";\ndatasource.Type = \"MySql\";\ndatasource.Domain = \"Cloud1\" // this has to be the name of the domain we added to the config\ndatasource.Path = \"path.to.mysql.database\";\ndatasource.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource);\n\n// Let's add a MySql database\nconst datasource2 = new ParkDS.Config.DataSource();\ndatasource2.Name = \"MyOtherMySqlDB\";\ndatasource2.Type = \"MySql\";\ndatasource2.Domain = \"Cloud2\" // this has to be the name of the domain we added to the config\ndatasource2.Path = \"path.to.mysql.database\";\ndatasource2.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource2);\n\nconst datasource3 = new ParkDS.Config.DataSource();\ndatasource3.Name = \"MsSqlDatabase\";\ndatasource3.Type = \"mssql\";\ndatasource3.Domain = \"Premise2\" // this has to be the name of the domain we added to the config\ndatasource3.Path = \"path.to.mssql.database\";\ndatasource3.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource3);\n\nconst datasource4 = new ParkDS.Config.DataSource();\ndatasource4.Name = \"MsSqlDatabase\";\ndatasource4.Type = \"mssql\";\ndatasource4.Domain = \"Cloud2\" // this has to be the name of the domain we added to the config\ndatasource4.Path = \"path.to.mssql.database\";\ndatasource4.Option = {} // This may be relevant later, see the Creating a Connector section\n\nConfig.DataSources.Add(datasource4);\n\n// Set up the Data Source Connectors\n// MyMySqlDB\nconst myMysqlConnector = require('./myMysqlConnector');\nconst mysqlconn = new myMysqlConnector(ParkDS.Config.Instance.GetDataSourceByName('MyMySqlDB'));\nParkDS.Connectors.Instance.Add(mysqlconn);\n\n//MsSqlDatabase\nconst myMssqlConnector = require('./myMssqlConnector');\nconst mssqlconn = new myMssqlConnector(ParkDS.Config.Instance.GetDataSourceByName('MsSqlDatabase');\nParkDS.Connectors.Instance.Add(mssqlconn);\n\n// Start ParkDS\nconst pds = new ParkDS();\npds.Start();\n// Connectors are connected, if websocket connections are required, they are started, if need be, the websocket server is running.\n```\n\u003ca name=\"usagePreviewConnectors\"/\u003e\n\n### Preview connectors\nYou have to write your own connectors to use ParkDS, below you have a couple of examples to see how the structure works, you'll also see that ParkDS.Logger objects are loaded in as well, more info about the logging system in the next chapter.\n\u003ca name=\"usagePreviewConnectorsMssql\"/\u003e\n\n**MS SQL:**\n```javascript\n\"use strict\";\nconst mssql = require(\"mssql\");\nconst ParkDS = require('parkds');\nconst EConnector = ParkDS.Connectors.EConnector;\nconst Log = ParkDS.Logger.Log;\nconst Entity = ParkDS.Logger.Entity;\nconst LogType = ParkDS.Logger.LogType;\nconst Config = ParkDS.Config.Instance;\n\nclass MsSqlConnector extends EConnector {\n    constructor(ds) {\n        super(ds);\n        this._entity = new Entity();\n        this._entity.Name = \"Ms Sql Connector\";\n        this._entity.Domain = Config.Settings.Name;\n        this._config = {\n            user: ds.User,\n            password: ds.Password,\n            server: ds.Path,\n            database: ds.Datasource\n        };\n        this._pool = new mssql.ConnectionPool(this._config);\n    }\n    /**\n     * Sends a query to the MsSql Database\n     * @param {string} query the query string to retrieve data\n     * @returns {Object} returns a JSON Object\n     */\n    Query(query) {\n        var resolve;\n        var reject;\n        var obj = new Object();\n        const ps = new mssql.PreparedStatement(this._pool);\n        for (var i in query.Bindings) {\n            var type;\n            switch (query.Bindings[i].Type) {\n                case \"nvarchar\":\n                    type = mssql.NVarChar(50);\n                    break;\n            }\n            ps.input(query.Bindings[i].Name, type);\n            obj[query.Bindings[i].Name] = query.Bindings[i].Value;\n        }\n        ps.prepare(query.String, err =\u003e {\n            if (err) {\n                Log.Register(this._entity, LogType.ERROR, err);\n                reject(err);\n            }\n            else {\n                ps.execute(obj, (err, result) =\u003e {\n                    if (err) {\n                        Log.Register(this._entity, LogType.ERROR, err);\n                        var e = new Object({\n                            Error: {\n                                Code: err.code,\n                                Number: err.originalError.info.number,\n                                Message: err.originalError.info.message,\n                                State: null,\n                                Fatal: null\n                            }\n                        });\n                        reject(e);\n                    }\n                    else {\n                        var res = new Object({ rows: result.recordset });\n                        ps.unprepare(err =\u003e {\n                            if (err) {\n                                Log.Register(this._entity, LogType.ERROR, err);\n                                reject(err);\n                            }\n                            else {\n                                resolve(res);\n                            }\n                        });\n                    }\n                });\n            }\n        });\n        return new Promise((res, rej) =\u003e {\n            resolve = res;\n            reject = rej;\n        });\n    }\n    OpenConnection() {\n        this._pool.connect(err =\u003e {\n            var e;\n            if (err) {\n                \n                e = new Object({\n                    Status: 0,\n                    Error: {\n                        Code: err.originalError.code,\n                        Message: err.originalError.message,\n                        Type: err.name\n                    }\n                });\n            }\n            else {\n                e = new Object({\n                    Status: 2\n                });\n            }\n            Log.Register(this._entity, LogType.ERROR, err);\n        });\n    }\n    /**\n     * Close the connection to the MsSQl Database\n     * */\n    CloseConnection() {\n        this._pool.close();\n    }\n}\nmodule.exports = MsSqlConnector;\n```\n\u003ca name=\"usagePreviewConnectorsMysql\"/\u003e\n\n**My SQL:**\n```javascript\n\"use strict\";\nconst MySql = require(\"mysql\");\nconst ParkDS = require('parkds');\nconst EConnector = ParkDS.Connectors.EConnector;\nconst Log = ParkDS.Logger.Log;\nconst Entity = ParkDS.Logger.Entity;\nconst LogType = ParkDS.Logger.LogType;\nconst Config = ParkDS.Config.Instance;\n\nclass MySqlconnector extends EConnector {\n    constructor(ds) {\n        super(ds);\n        this._entity = new Entity();\n        this._entity.Name = \"Ms Sql Connector\";\n        this._entity.Domain = Config.Settings.Name;\n        this._config = {\n            host: ds.Path,\n            user: ds.User,\n            password: ds.Password,\n            database: ds.Datasource\n        };\n    }\n    OpenConnection() {\n        this._status = 1;\n        try {\n            this._pool = MySql.createPool(this._config);\n        }\n        catch (e) {\n            Log.Register(this._entity, LogType.ERROR, e);\n            // Add logging\n        }\n    }\n    CloseConnection() {\n        this._status = 0;\n        try {\n            this._connection.end();\n        }\n        catch (e) {\n            Log.Register(this._entity, LogType.ERROR, e);\n        }\n    }\n    /**\n     * Sends a query to a MySql Database\n     * @param {string} query the query string to retrieve data\n     * @returns {Object} returns a JSON Object\n     */\n    Query(query) {\n        var self = this;\n        var binds = [];\n        var resolve;\n        var reject;\n        for (var i in query.Bindings) {\n            binds.push(query.Bindings[i].Value);\n        }\n        this._pool.getConnection(function (err, connection) {\n            if (err) {\n                Log.Register(this._entity, LogType.ERROR, err);\n                var e = new Object({\n                    Error: {\n                        Code: err.code,\n                        Number: err.errno,\n                        Message: err.sqlMessage,\n                        State: err.sqlState,\n                        Fatal: err.fatal\n                    }\n                });\n                resolve(e);\n            }\n            else {\n                connection.query(query.String, binds, function (err, rows) {\n                    connection.release();\n                    if (err) {\n                        Log.Register(this._entity, LogType.ERROR, err);\n                        var e = new Object({\n                            Error: {\n                                Code: err.code,\n                                Number: err.errno,\n                                Message: err.sqlMessage,\n                                State: err.sqlState,\n                                Fatal: err.fatal\n                            }\n                        });\n                        resolve(e);\n                    }\n                    else {\n                        resolve(self.ParseToObject(rows));\n                    }\n                });\n            }\n        });\n        return new Promise((res, rej) =\u003e {\n            resolve = res;\n            reject = rej;\n        });\n    }\n    /**\n     * Give an Array to receive an object the system works with.\n     * @param {Array} rows the rows needing to be processed.\n     * @returns {Object} with field Rows where all data is stored.\n     */\n    ParseToObject(rows) {\n        var result = new Object({ rows: [] });\n        for (var i = 0; i \u003c rows.length; i++) {\n            var row = new Object();\n            for (var cname in rows[i]) {\n                row[cname] = rows[i][cname];\n            }\n            result['rows'].push(row);\n        }\n        return result;\n    }\n}\nmodule.exports = MySqlconnector;\n```\n\u003ca name=\"logging\"/\u003e\n\n### ParkDS Logging\nParkDS comes with it's own logging system following the Observer Design Pattern.\nthe logger outputs an object with 3 properties: `Entity (Name, Domain)`, `LogType (ERROR, STATUS, TRAFFIC)`, and `Content`).\nTo add an observer to the Logging system, you'll need an object with an `Update` method that takes a `ParkDS.Logger.Log` object as an argument.\n\nParkDS outputs 3 types of logs:\n* **ERROR:** Any errors being thrown inside of ParkDS will go through here. ParkDS should not stop working when an error occurs, so we advice at least listening to these.\n* **STATUS:** this outputs any status changes inside of ParkDS (Connecter went down, WebSocket Connection dropped, etc.).\n* **TRAFFIC:** Outputs the traffic through ParkDS (if a package is received, added to the queue, send to another domain, resolved, etc., this happens for EVERY transaction you do through ParkDS and will be a heavy load when lots of transactions are run).\n\nWhen writing your own connector you need to load in `Entity`, `LogType`, and `Log`.\n\nIn your connector, add a property of type `Entity`.\n```javascript\nconst ParkDS = require('parkds');\nconst Entity = ParkDS.Logger.Entity;\nconst Log = ParkDS.Logger.Log;\nconst LogType = ParkDS.Logger.LogType;\n\nclass SomethingThatNeedsToOutputLogsInParkDS {\n    constructor() {\n        this._entity = new Entity();\n        this._entity.Name = \"name\";\n        this._entity.Domain = \"domain name\";\n    }\n    \n    Method() {\n       try {\n         Log.Register(this._entity, LogType.STATUS, 1);\n         Log.Register(this._entity, LogType.TRAFFIC, \"Method() is running\");\n       } catch (e) {         \n         Log.Register(this._entity, LogType.STATUS, 0);\n         Log.Register(this._entity, LogType.ERROR, e);\n       }\n    }\n}\n```\n\u003ca name=\"loggingObserver\"/\u003e\n\n#### Logger Observer\n```javascript\n\nclass LogObserver {\n    Update(log) {\n        var time = ('0' + log.TimeStamp.getHours()).slice(-2) + \":\" + ('0' + log.TimeStamp.getMinutes()).slice(-2) + \":\" + ('0' + log.TimeStamp.getSeconds()).slice(-2) + \".\" + ('00' + log.TimeStamp.getUTCMilliseconds()).slice(-3);\n        var output;\n        switch (log.Type) {\n            case LogType.ERROR:\n                output = `\\x1b[31m${time} [${log.Entity.Name}]: Error Thrown:\\x1b[0m`;\n                console.log(log.Content); \nclass LogObserver {\n    Update(log) {\n        var time = ('0' + log.TimeStamp.getHours()).slice(-2) + \":\" + ('0' + log.TimeStamp.getMinutes()).slice(-2) + \":\" + ('0' + log.TimeStamp.getSeconds()).slice(-2) + \".\" + ('00' + log.TimeStamp.getUTCMilliseconds()).slice(-3);\n        var output;\n        switch (log.Type) {\n            case LogType.ERROR:\n                output = `\\x1b[31m${time} [${log.Entity.Name}]: Error Thrown:\\x1b[0m`;\n                console.log(log.Content); \nclass LogObserver {\n    Update(log) {\n        var time = ('0' + log.TimeStamp.getHours()).slice(-2) + \":\" + ('0' + log.TimeStamp.getMinutes()).slice(-2) + \":\" + ('0' + log.TimeStamp.getSeconds()).slice(-2) + \".\" + ('00' + log.TimeStamp.getUTCMilliseconds()).slice(-3);\n        var output;\n        switch (log.Type) {\n            case LogType.ERROR:\n                output = `\\x1b[31m${time} [${log.Entity.Name}]: Error Thrown:\\x1b[0m`;\n                console.log(log.Content);\n                break;\n            case LogType.TRAFFIC:\n                output = `${time} \\x1b[0m[\\x1b[35m${log.Entity.Name}\\x1b[0m]: \\x1b[32m${log.Content}\\x1b[0m`;\n                console.log(output);\n                break;\n            case LogType.STATUS:\n                output = `${time} [\\x1b[33m${log.Entity.Name} Status:\\x1b[0m ${log.Content}]`;\n                console.log(output);\n                break;\n        }\n    }\n    constructor() {\n    }\n}\n                break;\n            case LogType.TRAFFIC:\n                output = `${time} \\x1b[0m[\\x1b[35m${log.Entity.Name}\\x1b[0m]: \\x1b[32m${log.Content}\\x1b[0m`;\n                console.log(output);\n                break;\n            case LogType.STATUS:\n                output = `${time} [\\x1b[33m${log.Entity.Name} Status:\\x1b[0m ${log.Content}]`;\n                console.log(output);\n                break;\n        }\n    }\n    constructor() {\n    }\n}\n                break;\n            case LogType.TRAFFIC:\n                output = `${time} \\x1b[0m[\\x1b[35m${log.Entity.Name}\\x1b[0m]: \\x1b[32m${log.Content}\\x1b[0m`;\n                console.log(output);\n                break;\n            case LogType.STATUS:\n                output = `${time} [\\x1b[33m${log.Entity.Name} Status:\\x1b[0m ${log.Content}]`;\n                console.log(output);\n                break;\n        }\n    }\n    constructor() {\n    }\n}\n```\n\nParkDS only logs locally, so the logging is decentralized, however, you can very easily send the logs to a central point. In your logger observer you can choose to send a log through ParkDS itself, and on the central logging server, create a custom connector that can receive them. This is not how ParkDS is intended to be used, but it is perfectly doable.\n\n\u003ca name=\"license\"\u003e\n \n # License\n \u003ca href=\"https://github.com/sneels/ParkDS/edit/master/LICENSE\"\u003eMIT\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsneels%2Fparkds","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsneels%2Fparkds","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsneels%2Fparkds/lists"}