From 7a0d1fc12b4b3c5ddb8e43686c91afb39af2a4a8 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 4 Dec 2020 14:14:43 +0000 Subject: [PATCH 1/9] add server from example app --- .eslintignore | 1 + examples/subscribing-example-app/.gitignore | 2 + .../subscribing-example-app/package-lock.json | 1128 +++++++++++++++++ examples/subscribing-example-app/package.json | 21 + examples/subscribing-example-app/server.js | 35 + 5 files changed, 1187 insertions(+) create mode 100644 examples/subscribing-example-app/.gitignore create mode 100644 examples/subscribing-example-app/package-lock.json create mode 100644 examples/subscribing-example-app/package.json create mode 100644 examples/subscribing-example-app/server.js diff --git a/.eslintignore b/.eslintignore index 4d10d30..dd0eb45 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ dist/ +examples/ webpack.config.js \ No newline at end of file diff --git a/examples/subscribing-example-app/.gitignore b/examples/subscribing-example-app/.gitignore new file mode 100644 index 0000000..1dcef2d --- /dev/null +++ b/examples/subscribing-example-app/.gitignore @@ -0,0 +1,2 @@ +node_modules +.env \ No newline at end of file diff --git a/examples/subscribing-example-app/package-lock.json b/examples/subscribing-example-app/package-lock.json new file mode 100644 index 0000000..4f60cd8 --- /dev/null +++ b/examples/subscribing-example-app/package-lock.json @@ -0,0 +1,1128 @@ +{ + "name": "deliveryapp", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ably/msgpack-js": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.3.4.tgz", + "integrity": "sha512-gmnsxxcN/8WfoxZxQQF9LvM3ZUbuVH0LCS6oX7EJS+VfkXWBFIgDV+h7a0sntwKSvAEg4uJzNDje7kpH8/LJ3Q==", + "requires": { + "bops": "^1.0.1" + } + }, + "ably": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.4.tgz", + "integrity": "sha512-RodcLKlxwFtnBxPhr/Lwr/K4NA/WveyO39cliO4J3qmJsSi3kPZDS2bEHjXGPrzKPi9+zOfJ8revEGa0+PcpuQ==", + "requires": { + "@ably/msgpack-js": "^0.3.3", + "request": "^2.87.0", + "ws": "^5.1" + } + }, + "ably-asset-tracking": { + "version": "file:../..", + "requires": { + "ably": "^1.2.4" + }, + "dependencies": { + "@ably/msgpack-js": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@ably/msgpack-js/-/msgpack-js-0.3.4.tgz", + "integrity": "sha512-gmnsxxcN/8WfoxZxQQF9LvM3ZUbuVH0LCS6oX7EJS+VfkXWBFIgDV+h7a0sntwKSvAEg4uJzNDje7kpH8/LJ3Q==", + "requires": { + "bops": "^1.0.1" + } + }, + "ably": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/ably/-/ably-1.2.4.tgz", + "integrity": "sha512-RodcLKlxwFtnBxPhr/Lwr/K4NA/WveyO39cliO4J3qmJsSi3kPZDS2bEHjXGPrzKPi9+zOfJ8revEGa0+PcpuQ==", + "requires": { + "@ably/msgpack-js": "^0.3.3", + "request": "^2.87.0", + "ws": "^5.1" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "base64-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", + "integrity": "sha1-R0IRyV5s8qVH20YeT2d4tR0I+mU=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bops": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bops/-/bops-1.0.1.tgz", + "integrity": "sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==", + "requires": { + "base64-js": "1.0.2", + "to-utf8": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "to-utf8": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", + "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI=" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "requires": { + "punycode": "^2.1.0" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "base64-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", + "integrity": "sha1-R0IRyV5s8qVH20YeT2d4tR0I+mU=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bops": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bops/-/bops-1.0.1.tgz", + "integrity": "sha512-qCMBuZKP36tELrrgXpAfM+gHzqa0nLsWZ+L37ncsb8txYlnAoxOPpVp+g7fK0sGkMXfA0wl8uQkESqw3v4HNag==", + "requires": { + "base64-js": "1.0.2", + "to-utf8": "0.0.1" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "to-utf8": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", + "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "requires": { + "punycode": "^2.1.0" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } +} diff --git a/examples/subscribing-example-app/package.json b/examples/subscribing-example-app/package.json new file mode 100644 index 0000000..57a5fce --- /dev/null +++ b/examples/subscribing-example-app/package.json @@ -0,0 +1,21 @@ +{ + "name": "deliveryapp", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "ably": "^1.2.1", + "ably-asset-tracking": "file:../../", + "dotenv": "^8.2.0", + "express": "^4.17.1" + }, + "engines": { + "node": "12.x" + } +} diff --git a/examples/subscribing-example-app/server.js b/examples/subscribing-example-app/server.js new file mode 100644 index 0000000..5e93786 --- /dev/null +++ b/examples/subscribing-example-app/server.js @@ -0,0 +1,35 @@ +const express = require('express'); +const Ably = require('ably/promises'); +require('dotenv').config(); + +if (!process.env.ABLY_API_KEY) { + console.error('ABLY_API_KEY environment variable is missing'); + process.exit(1); +} + +const client = new Ably.Realtime(process.env.ABLY_API_KEY); +const port = process.env.PORT || 5000; + +const app = express(); +app.get('/', async (request, response) => { + response.sendFile(__dirname + '/views/index.html'); +}); + +app.get('/mapbox', async (request, response) => { + response.sendFile(__dirname + '/views/mapbox.html'); +}); + +app.get('/google', async (request, response) => { + response.sendFile(__dirname + '/views/google.html'); +}); + +app.get('/api/createTokenRequest', async (request, response) => { + const tokenRequestData = await client.auth.createTokenRequest({ + clientId: 'ably-client-side-api-calls-demo', + }); + response.send(tokenRequestData); +}); + +app.use(express.static('public')); + +app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`)); From 1e9ec024ec0dcc1dc22eba9c508e6960351604f6 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 14:59:39 +0000 Subject: [PATCH 2/9] correct externals field for script tag usage --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index b54ce65..f93b2e7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,6 +12,6 @@ module.exports = { rules: [{ test: /\.ts$/, loader: 'ts-loader' }], }, externals: { - ably: true, + ably: 'Ably', }, }; From 441c22f664a52d9f8a6237ee0069f42efb1bc158 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 15:00:18 +0000 Subject: [PATCH 3/9] fix errors in subscriber SDK --- src/lib/AssetConnection.ts | 46 ++++++++++++++++++++++++-------------- src/lib/AssetSubscriber.ts | 4 ++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/lib/AssetConnection.ts b/src/lib/AssetConnection.ts index e87d6a9..fc4235a 100644 --- a/src/lib/AssetConnection.ts +++ b/src/lib/AssetConnection.ts @@ -51,25 +51,35 @@ class AssetConnection { } } - close(): void { + close = async (): Promise => { this.channel.unsubscribe(); - this.leaveChannelPresence(); + await this.leaveChannelPresence(); this.ably.close(); } - private subscribeForRawEvents(rawLocationListener: LocationListener) { + private subscribeForRawEvents = (rawLocationListener: LocationListener) => { this.channel.subscribe(EventNames.raw, (message) => { - message.data.forEach(rawLocationListener); + const parsedMessage = (typeof message.data === 'string') ? JSON.parse(message.data) : message.data; + if (Array.isArray(parsedMessage)) { + parsedMessage.forEach(rawLocationListener); + } else { + rawLocationListener(parsedMessage); + } }); } - private subscribeForEnhancedEvents(enhancedLocationListener: LocationListener) { - this.channel.subscribe(EventNames.raw, (message) => { - message.data.forEach(enhancedLocationListener); + private subscribeForEnhancedEvents = (enhancedLocationListener: LocationListener) => { + this.channel.subscribe(EventNames.enhanced, (message) => { + const parsedMessage = (typeof message.data === 'string') ? JSON.parse(message.data) : message.data; + if (Array.isArray(parsedMessage)) { + parsedMessage.forEach(enhancedLocationListener); + } else { + enhancedLocationListener(parsedMessage); + } }); } - private async joinChannelPresence() { + private joinChannelPresence = async () => { this.channel.presence.subscribe(this.onPresenceMessage); this.channel.presence.enterClient(this.ably.auth.clientId, ClientTypes.subscriber).catch((reason) => { this.logger.logError(`Error entering channel presence: ${reason}`); @@ -77,17 +87,19 @@ class AssetConnection { }); } - private leaveChannelPresence() { + private leaveChannelPresence = async () => { this.channel.presence.unsubscribe(); this.notifyAssetIsOffline(); - this.channel.presence.leaveClient(this.ably.auth.clientId, ClientTypes.subscriber).catch((reason) => { - this.logger.logError(`Error leaving channel presence: ${reason}`); - throw new Error(reason); - }); + try { + await this.channel.presence.leaveClient(this.ably.auth.clientId, ClientTypes.subscriber); + } catch (e) { + this.logger.logError(`Error leaving channel presence: ${e.reason}`); + throw new Error(e.reason); + } } - private onPresenceMessage(presenceMessage: AblyTypes.PresenceMessage) { - if (presenceMessage.data.type === ClientTypes.publisher) { + private onPresenceMessage = (presenceMessage: AblyTypes.PresenceMessage) => { + if (presenceMessage.data?.type === ClientTypes.publisher) { if (presenceMessage.action === 'enter') { this.notifyAssetIsOnline(); } else if (presenceMessage.action === 'leave') { @@ -96,11 +108,11 @@ class AssetConnection { } } - private notifyAssetIsOnline() { + private notifyAssetIsOnline = () => { this?.onStatusUpdate?.(true); } - private notifyAssetIsOffline() { + private notifyAssetIsOffline = () => { this?.onStatusUpdate?.(false); } } diff --git a/src/lib/AssetSubscriber.ts b/src/lib/AssetSubscriber.ts index 4dca0e2..a54083f 100644 --- a/src/lib/AssetSubscriber.ts +++ b/src/lib/AssetSubscriber.ts @@ -38,8 +38,8 @@ class AssetSubscriber { ); } - stop(): void { - this.assetConnection?.close?.(); + stop = async (): Promise => { + await this.assetConnection?.close?.(); delete this.assetConnection; } } From 299a472c7fe68e22e6779fe469988118b89cdfb7 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 15:00:41 +0000 Subject: [PATCH 4/9] add modified demo code only allow one vehicle at a time --- .../public/Coordinate.js | 64 ++++ .../public/Google/GoogleMapsMarker.js | 31 ++ .../public/Google/script.js | 20 ++ .../public/Mapbox/MapBoxMarker.js | 27 ++ .../public/Mapbox/script.js | 22 ++ .../public/RiderConnection.js | 45 +++ examples/subscribing-example-app/public/Ui.js | 42 +++ .../subscribing-example-app/public/Vehicle.js | 66 +++++ .../public/driverE.png | Bin 0 -> 1142 bytes .../public/driverN.png | Bin 0 -> 777 bytes .../public/driverNE.png | Bin 0 -> 1295 bytes .../public/driverNW.png | Bin 0 -> 1244 bytes .../public/driverS.png | Bin 0 -> 749 bytes .../public/driverSE.png | Bin 0 -> 1404 bytes .../public/driverSW.png | Bin 0 -> 1361 bytes .../public/driverW.png | Bin 0 -> 1109 bytes .../public/favicon.ico | Bin 0 -> 15406 bytes .../subscribing-example-app/public/icon.png | Bin 0 -> 1205 bytes .../subscribing-example-app/public/style.css | 277 ++++++++++++++++++ .../subscribing-example-app/public/van.png | Bin 0 -> 3159 bytes examples/subscribing-example-app/server.js | 1 + .../subscribing-example-app/views/google.html | 37 +++ .../subscribing-example-app/views/index.html | 16 + .../subscribing-example-app/views/mapbox.html | 39 +++ src/lib/AssetConnection.ts | 20 +- src/lib/AssetSubscriber.ts | 2 +- 26 files changed, 698 insertions(+), 11 deletions(-) create mode 100644 examples/subscribing-example-app/public/Coordinate.js create mode 100644 examples/subscribing-example-app/public/Google/GoogleMapsMarker.js create mode 100644 examples/subscribing-example-app/public/Google/script.js create mode 100644 examples/subscribing-example-app/public/Mapbox/MapBoxMarker.js create mode 100644 examples/subscribing-example-app/public/Mapbox/script.js create mode 100644 examples/subscribing-example-app/public/RiderConnection.js create mode 100644 examples/subscribing-example-app/public/Ui.js create mode 100644 examples/subscribing-example-app/public/Vehicle.js create mode 100644 examples/subscribing-example-app/public/driverE.png create mode 100644 examples/subscribing-example-app/public/driverN.png create mode 100644 examples/subscribing-example-app/public/driverNE.png create mode 100644 examples/subscribing-example-app/public/driverNW.png create mode 100644 examples/subscribing-example-app/public/driverS.png create mode 100644 examples/subscribing-example-app/public/driverSE.png create mode 100644 examples/subscribing-example-app/public/driverSW.png create mode 100644 examples/subscribing-example-app/public/driverW.png create mode 100644 examples/subscribing-example-app/public/favicon.ico create mode 100644 examples/subscribing-example-app/public/icon.png create mode 100644 examples/subscribing-example-app/public/style.css create mode 100644 examples/subscribing-example-app/public/van.png create mode 100644 examples/subscribing-example-app/views/google.html create mode 100644 examples/subscribing-example-app/views/index.html create mode 100644 examples/subscribing-example-app/views/mapbox.html diff --git a/examples/subscribing-example-app/public/Coordinate.js b/examples/subscribing-example-app/public/Coordinate.js new file mode 100644 index 0000000..6915461 --- /dev/null +++ b/examples/subscribing-example-app/public/Coordinate.js @@ -0,0 +1,64 @@ +export class Coordinate { + constructor(lat, lng, bearing = 0) { + this.lat = lat; + this.lng = lng; + this.bearing = bearing; + } + + get latitude() { + return this.lat; + } + + get longitude() { + return this.lng; + } + + get compassDirection() { + return Coordinate.bearingToCompass(this.bearing); + } + + toGeoJson() { + return [this.lng, this.lat]; + } + + static fromGeoJson(coords, bearing = 0) { + return new Coordinate(coords[1], coords[0], bearing); + } + + static fromLngLat(lngLatObj, bearing = 0) { + return new Coordinate(lngLatObj.lat, lngLatObj.lng, bearing); + } + + static fromMessage(messageData) { + return Coordinate.fromGeoJson(messageData.geometry.coordinates, messageData.properties.bearing); + } + + static bearingToCompass(bearing) { + if ((bearing >= 0 && bearing < 23) || (bearing >= 337 && bearing <= 360)) { + return "N"; + } + if (bearing >= 23 && bearing < 67) { + return "NE"; + } + if (bearing >= 67 && bearing < 113) { + return "E"; + } + if (bearing >= 113 && bearing < 158) { + return "SE"; + } + if ((bearing >= 158 && bearing < 203) ) { + return "S"; + } + if ((bearing >= 203 && bearing < 247) ) { + return "SW"; + } + if (bearing >= 247 && bearing < 292) { + return "W"; + } + if (bearing >= 292 && bearing < 337) { + return "NW"; + } + + return null; + } +} \ No newline at end of file diff --git a/examples/subscribing-example-app/public/Google/GoogleMapsMarker.js b/examples/subscribing-example-app/public/Google/GoogleMapsMarker.js new file mode 100644 index 0000000..b0a9389 --- /dev/null +++ b/examples/subscribing-example-app/public/Google/GoogleMapsMarker.js @@ -0,0 +1,31 @@ +import { Coordinate } from "../Coordinate.js"; + +export class GoogleMapsMarker { + constructor(map, markerCoordinate) { + this.map = map; + this.current = markerCoordinate; + this.lastCompassDirection = "N"; + this.marker = new google.maps.Marker({ icon: "driverN.png", map: map }); + this.map.setZoom(16); + } + + getCurrentCoordinate() { + return this.current; + } + + updatePosition(targetCoordinate) { + this.marker.setPosition(targetCoordinate); + this.current = targetCoordinate; + + const compass = targetCoordinate.compassDirection; + + if (compass && compass !== this.lastCompassDirection) { + this.marker.setIcon(`driver${compass}.png`); + this.lastCompassDirection = compass; + } + } + + focus() { + this.map.panTo(this.current); + } +} diff --git a/examples/subscribing-example-app/public/Google/script.js b/examples/subscribing-example-app/public/Google/script.js new file mode 100644 index 0000000..c94906a --- /dev/null +++ b/examples/subscribing-example-app/public/Google/script.js @@ -0,0 +1,20 @@ +import { GoogleMapsMarker } from "./GoogleMapsMarker.js"; +import { RiderConnection } from "../RiderConnection.js"; +import { Coordinate } from "../Coordinate.js"; +import { bindUi } from "../Ui.js"; + +(async function() { + + const position = new Coordinate(0, 0); + const mapElement = document.getElementById("map"); + const map = new google.maps.Map(mapElement, { center: position, zoom: 3 }); + + function createMarker(coordinate) { + return new GoogleMapsMarker(map, coordinate); + } + + const riderConnection = new RiderConnection(createMarker); + await riderConnection.connect(); + + bindUi(riderConnection); +})(); diff --git a/examples/subscribing-example-app/public/Mapbox/MapBoxMarker.js b/examples/subscribing-example-app/public/Mapbox/MapBoxMarker.js new file mode 100644 index 0000000..f98aad4 --- /dev/null +++ b/examples/subscribing-example-app/public/Mapbox/MapBoxMarker.js @@ -0,0 +1,27 @@ +import { Coordinate } from "../Coordinate.js"; + +export class MapBoxMarker { + constructor(map, markerCoordinate) { + this.el = document.createElement('div'); + this.el.className = 'marker-mapbox'; + + this.map = map; + this.marker = new mapboxgl.Marker(this.el) + .setLngLat(markerCoordinate) + .addTo(this.map); + } + + getCurrentCoordinate() { + return Coordinate.fromLngLat(this.marker.getLngLat()); + } + + updatePosition(targetCoordinate) { + this.marker.setLngLat(targetCoordinate); + this.el.setAttribute('compass-direction', targetCoordinate.compassDirection); + } + + focus() { + const position = this.getCurrentCoordinate(); + this.map.flyTo({center: position, essential: true}); + } +} \ No newline at end of file diff --git a/examples/subscribing-example-app/public/Mapbox/script.js b/examples/subscribing-example-app/public/Mapbox/script.js new file mode 100644 index 0000000..f47a2aa --- /dev/null +++ b/examples/subscribing-example-app/public/Mapbox/script.js @@ -0,0 +1,22 @@ +import { MapBoxMarker } from "./MapBoxMarker.js"; +import { RiderConnection } from "../RiderConnection.js"; +import { Coordinate } from "../Coordinate.js"; +import { bindUi } from "../Ui.js"; + +(async function() { + + mapboxgl.accessToken = '***REMOVED***'; + + const position = new Coordinate(0, 0); + const mapElement = "map"; + const map = new mapboxgl.Map({ center: position.toGeoJson(), zoom: 15, container: mapElement, style: 'mapbox://styles/mapbox/streets-v11' }); + + function createMarker(coordinate) { + return new MapBoxMarker(map, coordinate); + } + + const riderConnection = new RiderConnection(createMarker); + await riderConnection.connect(); + + bindUi(riderConnection); +})(); diff --git a/examples/subscribing-example-app/public/RiderConnection.js b/examples/subscribing-example-app/public/RiderConnection.js new file mode 100644 index 0000000..03fd01b --- /dev/null +++ b/examples/subscribing-example-app/public/RiderConnection.js @@ -0,0 +1,45 @@ +import { Vehicle } from './Vehicle.js'; +import { Coordinate } from './Coordinate.js'; + +export class RiderConnection { + constructor(createMapSpecificMarker) { + this.createMapSpecificMarker = createMapSpecificMarker; + this.assetSubscriber = new AblyAssetTracking.AssetSubscriber({ + ablyOptions: { authUrl: '/api/createTokenRequest' }, + onEnhancedLocationUpdate: (message) => { + this.processMessage(message); + }, + onStatusUpdate: (status) => { + this.statusUpdateCallback(status); + }, + }); + this.shouldSnap = false; + } + + async connect(channelId) { + if (this.assetSubscriber.assetConnection) { + await this.assetSubscriber.stop(); + } + + this.assetSubscriber.start(channelId || 'ivan'); + } + + processMessage(message) { + const locationCoordinate = Coordinate.fromMessage(message); + + const riderId = locationCoordinate.id ?? 'default-id'; + + if (!this.rider) { + const marker = this.createMapSpecificMarker(locationCoordinate); + this.rider = new Vehicle(riderId, true, marker); + + marker.focus(); + } + + this.rider.move(locationCoordinate, this.shouldSnap); + } + + onStatusUpdate(callbackFunction) { + this.statusUpdateCallback = callbackFunction; + } +} diff --git a/examples/subscribing-example-app/public/Ui.js b/examples/subscribing-example-app/public/Ui.js new file mode 100644 index 0000000..4ccb942 --- /dev/null +++ b/examples/subscribing-example-app/public/Ui.js @@ -0,0 +1,42 @@ +export function bindUi(riderConnectionInstance) { + + var queryParams = new URLSearchParams(window.location.search); + + const updateChannelButton = document.getElementById("updateChannelButton"); + const channelIdTextBox = document.getElementById("channelID"); + const animationCheckbox = document.getElementById("animation"); + const subscriberCount = document.getElementById("subscriberCount"); + + if (!updateChannelButton || !channelIdTextBox) { + throw new Error("Where has the UI gone? Cannot continue. Can't find ChannelID"); + } + + animationCheckbox.addEventListener("change", (cbEvent) => { + riderConnectionInstance.shouldSnap = !cbEvent.target.checked; + }); + + updateChannelButton.addEventListener("click", () => { + const channelValue = channelIdTextBox.value; + if (channelValue.length > 0) { + riderConnectionInstance.connect(channelIdTextBox.value); + } + }); + + if (queryParams.has("channel")) { + const channelId = queryParams.get("channel"); + channelIdTextBox.value = channelId; + riderConnectionInstance.connect(channelId); + } + + riderConnectionInstance.onStatusUpdate((status) => { updateDriverStatus(status); }); + updateDriverStatus(riderConnectionInstance.driverStatus); +} + + +function updateDriverStatus(status) { + const driverPresent = "Driver is online"; + const noDrivers = "Driver is offline"; + + const message = status ? driverPresent : noDrivers; + subscriberCount.innerHTML = message; +} diff --git a/examples/subscribing-example-app/public/Vehicle.js b/examples/subscribing-example-app/public/Vehicle.js new file mode 100644 index 0000000..16518c8 --- /dev/null +++ b/examples/subscribing-example-app/public/Vehicle.js @@ -0,0 +1,66 @@ +import { Coordinate } from "./Coordinate.js"; + +export class Vehicle { + + constructor(id, follow, markerWrapper) { + this.id = id; + this.follow = follow || false; + + this.marker = markerWrapper; + + this.moveBuffer = []; + this.animationRateMs = 33; + this.animate(); + + this.movementsSinceLastFocused = 0; + this.numberOfMovementsToFocusAfter = 100; + } + + get position() { + return this.marker.getCurrentCoordinate(); + } + + async move(destinationCoordinate, snapToLocation = false) { + this.movementsSinceLastFocused++; + + if (snapToLocation) { + this.moveBuffer = [ destinationCoordinate ]; + return; + } + + this.moveBuffer = []; + + const currentCoordinate = this.marker.getCurrentCoordinate(); + + var path = turf.lineString([ currentCoordinate.toGeoJson(), destinationCoordinate.toGeoJson() ]); + var pathLength = turf.length(path, { units: 'miles' }); + + var numSteps = 30; // This is the FPS + + for (let step = 0; step <= numSteps; step++) { + const curDistance = step / numSteps * pathLength; + const targetLocation = turf.along(path, curDistance, { units: "miles" }); + + const targetCoordinate = Coordinate.fromGeoJson(targetLocation.geometry.coordinates, destinationCoordinate.bearing); + + this.moveBuffer.push(targetCoordinate); + } + } + + async animate() { + if (this.moveBuffer.length === 0) { + window.requestAnimationFrame(() => { this.animate(); }); + return; + } + + const targetCoordinate = this.moveBuffer.shift(); + this.marker.updatePosition(targetCoordinate); + + if (this.movementsSinceLastFocused >= this.numberOfMovementsToFocusAfter) { + this.movementsSinceLastFocused = 0; + this.marker.focus(); + } + + window.requestAnimationFrame(() => { this.animate(); }); + } +} diff --git a/examples/subscribing-example-app/public/driverE.png b/examples/subscribing-example-app/public/driverE.png new file mode 100644 index 0000000000000000000000000000000000000000..6a84160c6fb95d7131179c5a1608bd3dbab78484 GIT binary patch literal 1142 zcmV-+1d02JP)d>3p`D;9;ah}s2gfWih9A)(SPDzQKT!~z5=sD)N) zY4|09m{w|tjHM)`c0P9O#DBl%;XXUD9mlN_vjaw&cSmpT+;h*oGj|^FfBUyLwm9H# z!AiyqUiMbCIqC{^O~oo+vY8}iq&JbN4fq7Updbfp$^Q2MI~SMv;oWC2u_|ExI;6CM zkfepF7T{iX1XHnCeCy1o$6AbuQ=a$o`*GVQsE@HQy5fq}RO|Q3TADDt^EJ~&&pYo2 zl7^rDcnNo`2so3h>lPb#{WGyJhLGnGeAfNVPOq@XJMjvRhI(+{Y!3|8{6+QAzDaV+ z(Vxq{cr6m8hrp0&aN(x&!L>s0^1C9@(lDeG$Klrq=%eu^vY)dzkjgX8Wg?DvI#xSf z&KG2^GN(=nu#$2m8g!6-f!unNGL9D+&y0%3xw;O7W(IFbs9Y^EtqZ}r9mG%oQ)D;; z=V7-$fUu=`iC;B^>D>D^0v8Ivdb3^%WhRl438efS-=cY~dvMirjMCPE#D-f32HcX!drcqRJrGJbT%*XRb30BI>t}ApMouPHD?WJ>^rTU&Li_Un7 zC1=%Dx|k5>)ss|Xs4-{Lh3-RP2O5=_Tm;St*}Suc9NiW`-MmPO(Z$3uMR!h?RbDC8 z^`io*g`u7RTFtbRX(}l6GETQ!nBY=m$NN4^7J=!4fIy4ZNDOZet-<#*HY5}c9J&;q zaQ=lYjj6)5$d?wX#oAG>3mf?b5)yg_n=zI&LSiyDm?C(tEeNYoRf2+YU~Ut^PmeU+ zyprAI-2LQZu+-ZyI&uT2E}i2i>u>yK)XUq8U=@@XLDlVTF|p~V`#kpb_v{r9Z0SNG zHjlw87tuS~A2_^z;!Z;3MPNCHBNjcHeu*|b-n`NCjALzcUN!4F?!v(3?{dMk^lF=4 zUZOKqRJ@8BQwpok2>P?0&h4VnY=OaKhSTMSuiyV8*ulF@&Et;07*qo IM6N<$f|tN6P5=M^ literal 0 HcmV?d00001 diff --git a/examples/subscribing-example-app/public/driverN.png b/examples/subscribing-example-app/public/driverN.png new file mode 100644 index 0000000000000000000000000000000000000000..ee781f8f417f29cf30d9b15467f52da129f77aa9 GIT binary patch literal 777 zcmV+k1NQuhP)&>R802*eWbX!L09 z=fPqHtpy;9(1q)JpPNe~9xmV<1Rj`sFHaXuq#n&ZTK6%&xe@)fL!*O>bbV|^OybgE z`K%_ae+MQ@v?oQJG(fin=bbZ`*>^9Tvsn0$er_s&<@tPW=1OMb?Ws9zNN?VnIEeh| z;Z`j(@mTXE+JYaoCe{>86U(Hs+>9hVA`+UUH$!ob}> zK->s9+iz70q@RjO&EtCLEmRebcYE1Lg#y+iZ^4@Q{B&cn9pc*Ep<8HFPVE>|NgVrf z&lEAdpS%Q?;}lN{Z2=M{vHzBPUHCo00000NkvXX Hu0mjf0LNj> literal 0 HcmV?d00001 diff --git a/examples/subscribing-example-app/public/driverNE.png b/examples/subscribing-example-app/public/driverNE.png new file mode 100644 index 0000000000000000000000000000000000000000..5be56303659ee93a09cbe6363d1eea3815570eda GIT binary patch literal 1295 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 literal 0 HcmV?d00001 diff --git a/examples/subscribing-example-app/public/driverSE.png b/examples/subscribing-example-app/public/driverSE.png new file mode 100644 index 0000000000000000000000000000000000000000..039d01c9dcb688f604c4bc68b9ee4d79d9bbaa23 GIT binary patch literal 1404 zcmV-?1%vvDP)n_Nh~$el&Lkc)Z8>3w*IJAo1;XAIw4l8nXRp`X`9w6MHVy-ojD~MPC+F> zF&KLCrWeQo9tU^#{QBO*0e8FwI8DlgncA^ zjLRa4|3geJjilSBgtRHh*APCTFr45rDIilNW(T0BTPsCeJx$LYp&;aO*|;~gZ-?f}Mpis|^arXIR zxTbI*^+Gy|)vw~O&^08jfKsW1R;z&^ir_g89HDIvxxVk{@)xRiQh8e6885jsGRatQ zE7Ky*gO8sdp6CP76tNTWU1tG7AVMvCeRUAsq&&~d;6wo(vlGFKvvzc4;+e6D-4_4d z#pAwJF<`Catw6gjjxtb?w`v!>j%ZXPw6g{ZrAYh&_(2e<&e4O_*R!$h_ZSLHqsE6v zh3zAZvYKZMjddN$f8O}hi1f?xC^Z?pV@*i479zI37@|{xO{qf5qD&Yc6`|MKE0YO4 zSxZ_)wOqvZ(qzQl`;1~}lh*`}9Kz!ZXBJ+jmdttO?}p{Z?cKVNW9v6=Lg_DscwbwJ z7el&6loE$tzXVhm^Kn0D8P=UlKwiX;PzQ%$QD-F*>c7I=N2g^MlGg`BId?G(Jlp9~ zKnw42`{5K^>+0F_c)fqXTgz9kK}2*c3eQ|d+U#~x)w`?|eXR()e*$ycYS6C>fwR+$ za8n6ZQ@fgBt(7hGf}5Nhkauw{w%Q@({eesJr=5~?{b5_{4-IvY@3qBUS7_E?xnKCyOAqi32JRJVO{rWY?*I{mB^cQK?vD?0p|{7 zz-&lB=!5Si!SVPMiR~s!O~bu=DP`zD+)%FPTSS4 z5tp=7pl(?e6%!ZR*ia9?+id-Iab2<5ZuPe_Y+6|W%S7`33NQe02E-bj#gw`L0000< KMNUMnLSTYF^q7?Z literal 0 HcmV?d00001 diff --git a/examples/subscribing-example-app/public/driverSW.png b/examples/subscribing-example-app/public/driverSW.png new file mode 100644 index 0000000000000000000000000000000000000000..e45bba62d17221f470c95ca11e6ba7d13e0c7b51 GIT binary patch literal 1361 zcmV-X1+MyuP)*$@&nfFOxXg zn5H2qAUV!uLiwM7$;;K`<;SG%B5`rhY-HMdX2wZA%d!~?m7{>U;9+d&0qXN)72B`EFw06c2m0AS0q{l2Dks4!giJP-t zE}FdKcuE-1Mgbns-rI=?oB!?&G^>)YZN|Oh7P|M?qV{VfplLKn0;j_ zn{1W;R+%&)z%J+c2x^R~l;{ddquje-)II91cih6<&o=1-VDdB&0?LWzUr$sXz_!vP z$or}xHklX8KN72UWVR>+m2@|o~PI;7d2}cae*Wv84o8uj=yhrKrr#m>EFRL!_{Vl!_(Ib zQ`g3t;9Hj6F)*f1!m^pN4W$+1I1(VT$w)*R1jIrXmah?|S$J#x> z+cnUO;{g+vWTu+~L>9!ooNcB*-#Y3UdvSPGXTIa}W!{15n*%MS zRGVICirh;lSU`ABAswb03rfPmzeB*VU=y&5OunFLMY~U{r{s@>X7Fb7mPx%{MPpS~ z*)wM%m3ICs=?_$ID1*u)2s~Y!!2?$i*`5OlubE+|DWH|^2#wxyh+!m=NapLJkEDp? zNJ)D;pZXPDI8Xqg37PKa@`%O4eY zWQg_B!b!|3Xq}z3`BMaHboBO`4<^ebM}aqJfzOmasSh=WyhR0F13n}es^3091No$* zn#}8nL11DU`a0VXHCg}34tZ~GO0gY+bo8-Dp(iG`glz|f(K0?jjMGY4y3pr|?x=SW zov;pdm4BkS_G&U6n9Ak6*%oj$e-H-lAa+hOB9&SR`NF2JIHQ@zS%tra2n_wOVP? z#zaU%x@|Y1cC8zeO?Ef4KQnVZJG-0Nq}eptM|I%NnZ0|?`OeRG&kXSY^>4q}P7U~3 zNYJnwJa1i)IxV4~v;jfSOAV{+45Ha>%Z@tgRnut06Ffb z)r$-Qv@K_WRy+Hl?`bMxAuk(PPToR|-@4b#IEgpN^IH$%J?Dj6d6%Xp;1Q&16S(1M zJLX5sgi0K;s>BK$*M=~{p9UAmljBI(bt6~f2 z`1*N_Dgj(-f0f@v8%GW$Lb#)A)>2u7g<1&bCzUdvedJ7TvNw{U|KUnGQ@FHYjC zic`1|=mQfihKeLvk#;pf<=dM?R_!lXWO&vn8WWsysafU=|BjZAAH#Ry+tA1(V^(0rMp(Yj*`Ph-sKE&=laCB-W)q5PIYz}T85203 z)*zCsmKe@lt--I0RZuh$HL`-^d+wman}VTG%s*z9ytoQp@_1{-j?-7_aVKeqUC3ac zJBpJn_u#adv#toiyk zAIq25XBX~1bE&Pa^GF}-+1G_wbP?mDKVWG7>fPt|hU-X?o2&+2tB~NIi0?Yyc&Ou; z*H>%Mw56j9lfPWdq`COI1*)`m)Oeo78^IY^Bqdg=qV!o?SGOqH?65hUsBLV<>{MtJ zESzO%=)P0i+THV{B-_3{(0iCU+!dIcoyOdanJ@{PZECS1A4$=&_Rio}{#1B7>zJ0%uPh^k zuRqZOU0z`krVrECbaipD>+2-sxy|KsoO5h#ZM&KpULdw6{`Hdlzd< z^=zJ3aNW3(iT_MVh`c}0QM=O4MnB8WCY~@!W7)|rcA-uk`t9T~$!#)m;592t)dWxX zB1nV_p^uz`FwG3`6sw3e;1fo<)=(Dst%5+Z62Xd@7!o}NGVvU6JSW}~a6f^}w*q2$ z4Ln91hV!_Sa30TaqFqw?ofF_E?S&*}CxT>^Q213q?zaVT`Zc$N{1xyE*b1)^4e&~7 zfLs1axW0XwIQ1Cz$m2*&uEg?bYf(ITH3~B-5glnEN!%xCH^)#eG z2jK6&_bKG^J(!xe24~Bs;^3O4I99y_I(#EZbPNK303ed z1P2?XN_mUwH!&>s0;Y{ViZk1`qWN4UCXBufZAAGEx%@aZnwDpH*j_iu7KDelV0hwv zTtB!2cTR3>%T3*XpY1uPP%Pd_eVKkQCuzvvvAeAHpKInWI2;l%N^fhc@5*L>a7!vZ zYCtACdh358DB$4r#Fzz3#eBVo>D{uj8s_NcHt&^icQ10VwA6PujqQ}i?~DUEB9LId zPo3BQ3J);f;6D@e%VFSEzS}RmG3J|YnvankzwsT%cCqQv!=j&M&oJA57D+ZX77@LA z^>U;R|BemayLNp;+^5f5Nz9!6tO4VE33Yq1MP-OgkC#U3c-ay z8Sjt}$K@S6`VDHgf$8@zVP@8KfUS+iR1VwmLa@T;qC&fvrj$Z?wovDE{XUyJ0%tirjJBX zVLD>85x=@SYreJbAAiuoT(Qj2ZUns9^!w^P3;vR^NQxba(OILBKCA#Cfd%m4ya%4w zJYtRkzPC{_b^c@KQklV|qp~-_nN7dPg@}vFMM35$WDYODAXNdiy$cVw$u0K%6B^mB z??UXe7Jj}PAr-BLQoaabs(A=jErMuJDY%ha!He2S>>##--;S}ogX$)XZG!Nv5C>I3 z6jB3GKo!lo27&$`L9Lue_p<>0e#>ZIQ3gNZk`|G0Y@;`OJ|u#5kcld2zrggbghW~i zzIF%Pvl?lyaOTN=f%Xmq4f}@_IqJl2w&yr_}mH5(H0i`0KRgt|JgvroVy`y{xD_3%tT z2DedX;6CznJEus#7%R*cH|OR#K8DQ1q@jr{b> zwCA}Co$hRlK6YNCmnS`c=@wf15ZF0qXzerLB{sq{^AtR&E*RVZnPM-ohuHnQNU2R8 zuoFqqmDn`zLtLtvf~&QYacbiX>|OCOcC4tt?vg`TJhKUr5#JN%TZY6hX!Pc+hA?zL zJaayUSJrt5^vB_yaSooj7vMYi82sh*Jf(FnVIQ^cqvyI0Nl~>}J980^l}$xs=}hcd zxeWVDDsX8}Ep8m#gOWv8F*xc6#KvB1$;e#N$n&XzL~{;4X}2MZyaNC5%MgyZ4L|*L z1S&s8P{27Of#(rqfa;IS7pRT&0o0Fl!9nNf`*H$Pa(Ck6$yHc6eKWSM*oNx|YVh|9 zoAA}~ttk8G5~8BM$KdGmEh*^>8hw4YKpFBGRNC(l8g>^UVc$WeyN3|XUC@6Ch`R_i z$nU63eW)C2&@t`zsBF|T{nYAjq1D_$eDs${8S*&_N8Z5h(!==2#f^A$y^Pl1g0jVz z5TX07O{Hr1hreRfK@t5uhKAmueeRDh!L*m|*pJb5tp7;w){oE+t7%g zudS!`OY!9~dNvEswuOdXc_a{&pL22^K9SE~N9%t><9>RrMCu;WH@BJAKZc*Kt|K3m zww>Ft@wb`dD<1j^SD$ltpEI^^-$W-*&&3D{`SP`{&D=`_t)EZp?M@Q>trrg7dISp+U9~b{L zW8Ab8`6)%CBQ}Xd5WN*D{TxWy{T@LR}F0)bUWvTKB2_l$BM~J}!6VK7pW$sCkS~ShJ6T(HYBC z?b1|#UOwr|v{mgBi%Mz({pZe*Ns{{oK{7mps_XX;QGO>zvn)i%X%x$tlCt z#zxTXxrI9wogV1)K&J=(U=J|ySPIdyQi#qt2|1zj>h!?>9}h5lF(wZ5mv-K4uRpa% zBi-x1nE9DFAG4lmZAQNG`V=UCvqz5}=9ZSdc{Y7}g$(Ftrna@V2<+RpH<#jelzsY- zUCh3@`(OUT8rc1>e+@JsM9IRT7+~((n=V?oW2}@Z6M{FayIWhjI1W&FxjRht6MEGJD+FI?L!@7; zmEvpu67OAnuIpGwM_ZAFg+-6{Ioih>=KFWc-OSCqv+ep@jdpdiJuMNizX=QW{YIk_ ze;p{}U*mbZ>~$GvGtSDYr}1UTo7( zH>;nOhS0tMMsMOp?TqfpSZ1yP{k8L*9m7_M_(AnLb-;sBnR?7E%E6qeIT&9sv@Ice z&@UpsEDqBr zq+`L1e3F^SPD%JprO^H2#g1-vb!PXhQ`MHCKE+IavK=i>}WUqnr*-6 zJ$=oC%Ix|Nfs5lvcu`)K<2e-q-a81CWg;Oa1EaHZFzM|Al6gqdXHvX1lVZH1sb3Mr zmZyWmp7sO}Dl?GIyIs#EX=FOdj+pd}qDao(i#WECvOqBP}VD{GLbTBYQ+P zqP6K1OU;M3$2(xV7PdOsWi;7ZN7lPI6&T{&BA-*P0xBM*}Rx2MxcBt#n(R}``6L^QA}ODvejSm zev@1_w%*fy7Uct$ko_CUZYB<1MN}JOM*Pq@ST}+;NJbAA#5K{d5nMM~K}C@1b>+8`xr4UrMn*V{CpC*-KFD zUn;Jlw#{@7pn}d6l(uTa7d6G`i|RQZ#q>N@K}vZFW2~Nu*E9D~Nilo!P4HH5^mTB{ zX@tw$WXC)0ws(x%o64qpdMbv`#Pf}D{Kt~XCy@Q#$#w9J*a{#0F7Sr#gCw*T{^T?gbF{W07FJqIS=;Hx6r z<0$r?&g2}98DjqItOmG@rRVUhO-%i{{KNCP2Vg2$1ff_xS)K!}qriPu|p&SFos_$y50Htfc2f zw$r`KB$PAoqdX72GrZV6aLa8p*zTED4{jXuz8|1-8uWgcd_19}jV~{|jG;UP`9Mmx z>onCE5m$rUXgG7?mPfXaWBvxt z(LI=;dLg5GIj&<)!z;TH${{;3BEAB%$F9eUX=PYFvxHb{U`5etES|UuAB%BeShk(BvQhGj8`3{If_ktgHi1H-NJ4$P% zQtoN!5paf4?kM*p+zZZ7+eymdoM`7b$ydWG?Kn6ojSwUqMtJ;oj85Bt^>deF|Ek4k zC|!t#l7)0$W+CNv7NL5<60DuE1|PpygLxAV;@!~~kQ9Fh;gmlJ55LkH9aYs7r=MBx z=`jmFJbHg*WP3;*`0+>Jo_`kXytD91I|;s?Y#)Bys2h0}+@xcWs1MM&oqfdKcJ`2b zIevydln+wwLQG^0CgrTb&ZTp4zN!dUw@ks6+9^0!QG|xIvq`@Mn~OJK+luYjy!c_FgnmDxpopAaP93B9%klNtM$ypQu0lW=A8M4Ydjg2u90*!Rg2 z>|Irc!y9UFZu@o|-+UNV%P&#f|1QPY4y>-Zttxc&Z^Xk34R)g$%)=2G2o5@h!J0{#7>sqw(Y2pHW6q!j8_G;SMt_^? z#&%=fI6hQ$6S_gykr;gm1u3T~CwvT3==WvrghrGXAH>DoTXBzU|LN*_{CIgIZq@H3 z`_CBcXWoB}=2ELJYJF2=OkusRu$tb9V+c{-LFk}n1M0A5sKdU8dJqv#XqfsvgsGbi zs7V_6p6Wjk)aS9z*k;uKKzgQ6M`#Y3Uo+?)A}BW*8+8{6(RVO{{4{CQXDDBK5`Sx~ z#Y4)iJ-Sgs_HQuQzpdm93bMbZv0t|-l_wwh%a>dfiAU@d3Muz4KSBB*Flf+&r_g8~ z(7hUA+B%kveV^nQpC4;%>p0FdUei8Tc=!Y8v=1mJ`2b0A_c5X18&obkhp$i6;^*t@ z@JqY>H_887*PO!0j9YEN%Bw$1B)h)i@a9!HyT%py_^c!UH0$CeWB=>TwZ7_n5!#=gv;X1MGRn=a#g~m`za8APp>@HO4G#xvR($2{xp0@O>-%pz zIZYOtnGNdc;qf6PlD%aA{XfipUAz6`$^Yd`&f|8&W_)v^6xR-{ZauVd`Tf!lmtQW- zE#4I*pIGAPm^*gBfKk4-wr`uUSm@&FIvpa>wm;ndczS=vXOiI?f%)oGCTLv7DVJfO4v2|M#ywS8d8!zf5ZB z{ny7lKyLcowz-qfw5?yb{@0JE&%Hk}ckFEI()GXxmpkThOzopBbG?CqDdxDKfLOo6yI@Be%PUho12{6A$jZ cIz7(!1UwlG!U4Wg;RT$$JC&+T&V7F;6#3%golQLW{nvBg+}(8g z{-^Qy)dF)oy5mzgzWW{IiK&<drwzN-z1axu~;ndQBi!m?B!Unu_PvE=I~iAm;bF=UHY29={6X&P17}E2V5bN zI4oto*`bW1o1PBu+w$0N4_M7|u!I1mT0qCLz?ufF3*?UiU>P{solPY286W=x4_${M zHV~d@;bc7lG1fyQ--5ChA3A59jr3;{#2`2p7l3K*L}eV_Tp(~CBvC+U`yZIo?|T)} z-zP=Fn3)P~CbrdpcUm33!vJ?>NxwKJ-@OcxS$bVBW@o zr{}&r9CoVLN#|5V`|u3o8UO_Mw*Qh!Z)>tNk`CkTLNZZ8F?8EeJsy zQbGoMZ0PPoAdr>llFx#w@;IW6B6I#>=%c>k;P&~X@HG5%=Z0lMr{N==As!r^HgNQO zgIH}kC$`a}#eZ2Aikt)+kigeflzw56WchwZya*VZaqXGQ&&p8Bb(sifVuP-+nM#2r z#*1nd6YVyvRu9U-KKQLxi=tzoaSZm-V~{`m2%(sa+Rhf9KY5Hvtw9kF9-lsF|6J_w z>R(Jb<2_Ou&trG|26DIWxce%9KSOc-4@9&scFqWMyVYE7Qo6gMIHAe ziTF#`OQ-ev!!EIL@J8GAs~I+U%Q?!N9*Is+2#b@HJ!fB+PQTu2l`@^Phlg6XG|X2z zH=dV5;ic4Ab}<-^qF$?(Du)Nl2gS|8oAl^-dhDw}F!X5ry<6Zp4oZ2ybbPSCT(2Az zaE>=5k$5uCa@?XoJyqAVQoX7a-uz`0@|++nhN3aI^=h4VsaaPGxR(Eo{3pNwq%-bF Tdi}ce-aH!w)~~!H4J0m7Shk3DK*=4cHV6 zmgIMPq>&RXJ1wyiqMe~o$$qaaF+-Px=-PT;$$ppG@Vn?lt@>CByn;|WqOy&)XjIth zDhGIP`ZJ1 zdQve#sy153}IM&?)#hT?~X zsuw-SIvPDG1<&w&Cm>~G7KxT9i%9tbxGWT|uph1nI#eRc2^r!nNzo#kL!!=W`mNcM zNsTl^b$ezbGG-$*tbYd2gd3S)L+vc81smbbC=jU3`AEtj%L>N4Q9gI0MyE$wYD0AU z`t{#y?tK?)A3dEVW^i+gWIZu!RZTPRxw}qJrE}5Gu#>_Z`SjDLs!Fse9zPJK4hYnu zg@TTsS4~F53*C4k5poMt7`m)3(LlX-w!_a^&N}lxRJVF3BJ3vj47o?@z11&zMbq&w zdW_!u_^%QDPdDE0AA#X^Q;ZTss;9anz?DkjOB>n|>Dq+D_xub=RjUC}>c}Okd(!$Y zw0Y_f9ao^$*XJ4$6+I1DwO2r(At4V_b)wUhXkzLdp4qhrBE8Rt$dX3E zdlYZ+yy#yz@FWDE2U04H8KZonWk1pao;tQ89Dzi{#CfkJf;prU&Ku^v8;@aHNmn-1 zci_N*0|yQqIB?*=!T$j&dM&tjbqBk*V|CHr_&Zaf4)wdCVf}Tqu1qN^JMeepOFs%opM^u=SYY)%r4^(g<{A^L zh-gL6`lKP?PdjeI^paK3k)34@e0~xJ4Mt|jjnAEG#4j{9g4P!wuN_g(eGzfx9Ivv2y!}sT_3(}Dj}Ynwxq_!Am|!|$eN>TalCH^@`zkZe7&j5LFE&qgB6-^5=Yt@nTKW$Vi3GMO$OG3R+f8@Uxmk z5?sBjL90KdWDuhf(cd^({(i*fuGKS(p4<*}qciinR# z-yS>bXiN?qIB?*=fddB)960!2LfP*ye0A*}!#L3Ba^=_N2{{qWdUq;(hbp#qkN?JU z*OnWr)Prvbd-m)e7jjuX)(V5Yi8$r!2Hx4MO zeWGhOZUm0JpY^O8=Dv`|sTjg9K8fe{eivs$&G5y?u=}~+LP|~Lt3+Yxin*41jBJN(4s8;7j_e|1^NTRet0#}tqB)F7dh}py*+>Dza z>dqR)B5z*j99WT&1-ZTT;FSmyF@c6=(wU2lVhXse)Jx0J)J_da|_zq*WmQ=ery^%hTSxF;DQemnEj%EYkU;Li5ZMV8nOGDJ!o0B9PLyz zuMMBV{{CL1sXi4B)h;-m%njkVKOc4SpDka7TX%mM?z(z3HMiiEfj%4?9zu%x>d#Xt z`$Pw4rUg0>CK^lndLhd)#z&(VkA8s4N0`?iK!(PO(~}d>HMJt~Al%f#_;Wo))#}{jxFbY3aNxj!0|yQqIB?*=fddEj zu;9CNi`&h9($O(z@oJ&yHUFa&g_F01GtR6)0OM{Gs)|FRc)Oj?)hswua+%MEd)BO3 zeCzl^c&V-907f4>a3mHhY2sl)S)-URwJ6|1+Fkd93u|0ptC(mpVQO(8!{u@z(X}1h z9(xvlzV`ukv5)^PNVJ$RwOBCguLM(I<3bx3+_>;o7SUqD)S_UXkj5KlP?o`kH!i^0 zZcJR5notxo_paRhrcfYc={X^*juK89h8Y?6%kLHXO{Q~pk#<(*(1LAQshi^Ef?6W? znJ`OebMaOR~dis$62}q&AzB&J(z1vz}uyM;t?5IOCZV zDZ@TWUZV4ikp1i@7v8u4XWOE8u3XJ-X=?-j5?+|h5z{q9sjF$Cp1PTeY0^M`6APDV z1ty)hUEQ==;Ya`IK`g7UjYeVb7e zpRBzcE7xs6boeCNdOyO=R8(BRGKDVd3zRtl%qsG%$|%gqfJoim{^MjiFs*^s-#kgkB?R)U`euz x1sWQuZ*?OzGmYtZ%n`&KIB?+L65_uC3;^&fWO72TB6a`(002ovPDHLkV1i)i4-fzV literal 0 HcmV?d00001 diff --git a/examples/subscribing-example-app/server.js b/examples/subscribing-example-app/server.js index 5e93786..bf4f532 100644 --- a/examples/subscribing-example-app/server.js +++ b/examples/subscribing-example-app/server.js @@ -31,5 +31,6 @@ app.get('/api/createTokenRequest', async (request, response) => { }); app.use(express.static('public')); +app.use(express.static('../../dist')); app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`)); diff --git a/examples/subscribing-example-app/views/google.html b/examples/subscribing-example-app/views/google.html new file mode 100644 index 0000000..788e000 --- /dev/null +++ b/examples/subscribing-example-app/views/google.html @@ -0,0 +1,37 @@ + + + + + Map demo + + + + + + + + + + + +
+

Position Tracking

+
+ + + +
+

+
+

Animation:

+ +
+
+
+ + \ No newline at end of file diff --git a/examples/subscribing-example-app/views/index.html b/examples/subscribing-example-app/views/index.html new file mode 100644 index 0000000..3ac3c3b --- /dev/null +++ b/examples/subscribing-example-app/views/index.html @@ -0,0 +1,16 @@ + + + + Ably Map Demo + + + + +
+

Ably Mapbox NavSDK demo

+

The publishing driver SDK runs on Android and publishes the enhanced lat/long locations to an Ably channel, and these locations are shown on a map clipped to a road. View a demo using your preferred map provider with one of the links below.

+ View Google Maps demo » + View Mapbox Maps demo » +
+ + \ No newline at end of file diff --git a/examples/subscribing-example-app/views/mapbox.html b/examples/subscribing-example-app/views/mapbox.html new file mode 100644 index 0000000..6f88201 --- /dev/null +++ b/examples/subscribing-example-app/views/mapbox.html @@ -0,0 +1,39 @@ + + + + + Map demo + + + + + + + + + + + + + +
+

Position Tracking

+
+ + + +
+

+
+

Animation:

+ +
+
+
+ + diff --git a/src/lib/AssetConnection.ts b/src/lib/AssetConnection.ts index fc4235a..37091b7 100644 --- a/src/lib/AssetConnection.ts +++ b/src/lib/AssetConnection.ts @@ -55,29 +55,29 @@ class AssetConnection { this.channel.unsubscribe(); await this.leaveChannelPresence(); this.ably.close(); - } + }; private subscribeForRawEvents = (rawLocationListener: LocationListener) => { this.channel.subscribe(EventNames.raw, (message) => { - const parsedMessage = (typeof message.data === 'string') ? JSON.parse(message.data) : message.data; + const parsedMessage = typeof message.data === 'string' ? JSON.parse(message.data) : message.data; if (Array.isArray(parsedMessage)) { parsedMessage.forEach(rawLocationListener); } else { rawLocationListener(parsedMessage); } }); - } + }; private subscribeForEnhancedEvents = (enhancedLocationListener: LocationListener) => { this.channel.subscribe(EventNames.enhanced, (message) => { - const parsedMessage = (typeof message.data === 'string') ? JSON.parse(message.data) : message.data; + const parsedMessage = typeof message.data === 'string' ? JSON.parse(message.data) : message.data; if (Array.isArray(parsedMessage)) { parsedMessage.forEach(enhancedLocationListener); } else { enhancedLocationListener(parsedMessage); } }); - } + }; private joinChannelPresence = async () => { this.channel.presence.subscribe(this.onPresenceMessage); @@ -85,7 +85,7 @@ class AssetConnection { this.logger.logError(`Error entering channel presence: ${reason}`); throw new Error(reason); }); - } + }; private leaveChannelPresence = async () => { this.channel.presence.unsubscribe(); @@ -96,7 +96,7 @@ class AssetConnection { this.logger.logError(`Error leaving channel presence: ${e.reason}`); throw new Error(e.reason); } - } + }; private onPresenceMessage = (presenceMessage: AblyTypes.PresenceMessage) => { if (presenceMessage.data?.type === ClientTypes.publisher) { @@ -106,15 +106,15 @@ class AssetConnection { this.notifyAssetIsOffline(); } } - } + }; private notifyAssetIsOnline = () => { this?.onStatusUpdate?.(true); - } + }; private notifyAssetIsOffline = () => { this?.onStatusUpdate?.(false); - } + }; } export default AssetConnection; diff --git a/src/lib/AssetSubscriber.ts b/src/lib/AssetSubscriber.ts index a54083f..2d23558 100644 --- a/src/lib/AssetSubscriber.ts +++ b/src/lib/AssetSubscriber.ts @@ -41,7 +41,7 @@ class AssetSubscriber { stop = async (): Promise => { await this.assetConnection?.close?.(); delete this.assetConnection; - } + }; } export default AssetSubscriber; From d7658b5a090be7854c244cf3eefd18039b0f6d08 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 15:37:03 +0000 Subject: [PATCH 5/9] add readme for example app --- examples/subscribing-example-app/README.md | 426 +++++++++++++++++++++ 1 file changed, 426 insertions(+) create mode 100644 examples/subscribing-example-app/README.md diff --git a/examples/subscribing-example-app/README.md b/examples/subscribing-example-app/README.md new file mode 100644 index 0000000..5727f87 --- /dev/null +++ b/examples/subscribing-example-app/README.md @@ -0,0 +1,426 @@ +# Ably JavaScript Map Demo + +This is a demo of using an Ably data stream to place an animating marker on a map in real time. It shows usage of both [Mapbox](https://www.mapbox.com/) and [Google maps](https://developers.google.com/maps/documentation/javascript/overview) + +## To run locally + +First configure `TokenRequests` by creating an `.env` file in the root of the web folder and add your [Ably API key](https://support.ably.com/a/solutions/articles/3000030502) as below: + +```bash +ABLY_API_KEY=yourably:apikeyhere +``` + +If you are running the app for the first time, then install the dependencies, in the web folder run: +```bash +npm install +``` + +Then to run the web demos, in the web folder run: + +```bash +npm run start +``` + +## The Web App + +This is an Express app which serves an index page, and /mapbox and /google routes for the Mapbox and Google maps demos respectively. + +Each of these routes, maps to a HTML file in `/views/` - [google.html](views/google.html) and [mapbox.html](views/mapbox.html) respectively. +These two examples are useful for comparing and contrasting the different SDK calls required to use either mapping solution. + +### Application Topology + +We've split up the files required into the following locations: + +**/web/views/** - HTML files +**/web/public/** - client side assets +**/web/public/Google** - Google maps implementation +**/web/public/MapBox** - MapBox maps implementation + +Each HTML file references its own [JavaScript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) that executes when the page loads. This example uses ES Modules to reference the other scripts using `import` statements, without having to use a bundler, or Webpack, or any other build tools. + +The Mapbox and Google Maps `script.js` modules are very similar, and do the following: + +* Create an initial location to draw the world map at. +* Find an HTML element to display the map in. +* Create an instance of the respective mapping SDk. +* Implement a factory function which is responsible for creating a map marker (in a format that the active mapping SDK understands). +* Create an instance of a class called RiderConnection, and connects to it. +* Wires up the UI. + +For example, here is the Google `script.js`: + +```js +(async function() { + + const position = new Coordinate(0, 0); // Create start location + const mapElement = document.getElementById("map"); // Find mapping element + const map = new google.maps.Map(mapElement, { center: position, zoom: 3 }); // Initilise GMaps SDK + + // Create `createMarker` factory function that creates Google Maps Marker + function createMarker(coordinate) { + return new GoogleMapsMarker(map, coordinate); + } + + // Connect to Ably using our `RiderConnection` class + const riderConnection = new RiderConnection(createMarker); + await riderConnection.connect(); + + // Wire up UI buttons + bindUi(riderConnection); +})(); +``` + +and the very similar MapBox `script.js`: + +```js + +(async function() { + + mapboxgl.accessToken = 'access-token-here'; + + const position = new Coordinate(0, 0); + const mapElement = "map"; + const map = new mapboxgl.Map({ center: position.toGeoJson(), zoom: 15, container: mapElement, style: 'mapbox://styles/mapbox/streets-v11' }); + + function createMarker(coordinate) { + return new MapBoxMarker(map, coordinate); + } + + const riderConnection = new RiderConnection(createMarker); + await riderConnection.connect(); + + bindUi(riderConnection); +})(); +``` + +The only places that these two code samples differ, are the `createMarker` factory functions (which create either a `GoogleMapsMarker` or a `MapBoxMarker`), and where we call either `google.maps.Map` or `mapboxgl.Map` to construct the map. + +The logic that deals with creating and managing map markers differs between each platform, it is provider specific, so there are two completely distinct implementations in [public/Google/GoogleMapsMarker.js](public/Google/GoogleMapsMarker.js) and [public/MapBox/MapBoxMarker.js](public/Google/GoogleMapsMarker.js). + +These map markers act as [`adapters`](https://en.wikipedia.org/wiki/Adapter_pattern) - the rest of the code can interacts with the map markers in a map-platform-agnostic manner. + +This approach keeps the display logic out of the Ably code. + +### Map Markers + +Each of the PROVIDERMapMarker.js classes, implements the same set of functions: + +```js +export class FooMapsMarker { + getCurrentCoordinate() { ... } + updatePosition(targetCoordinate) { ... } + focus() { ... } +} +``` + +These functions will be called by `RiderConnection` to get current marker locations, update marker positions, and focus on markers. By keeping the factory functions in the provider specific `script.js` files, the `RiderConnection` code doesn't need to know anything about our mapping libraries. + +### RiderConnection + +The RiderConnection class is the heart of the application - it connects to the Ably channel dedicated to Rider GPS coordinate updates, and processes the messages as they arrive. +An instance of this class is constructed in `script.js` and handles most of the application logic. + +This class has to: + +* Connect to Ably using the Ably Asset Subscriber SDK. +* Processes messages from Ably. +* Create an instance of a `Vehicle` whenever a rider is detected. +* Track whether the driver is online or not. + +#### RiderConnection implementation + +The constructor is defined in [script.js](public/RiderConnection.js): + +```js +export class RiderConnection { + constructor(createMapSpecificMarker) { + this.createMapSpecificMarker = createMapSpecificMarker; + this.assetSubscriber = new AblyAssetTracking.AssetSubscriber({ + ablyOptions: { authUrl: '/api/createTokenRequest' }, + onEnhancedLocationUpdate: (message) => { + this.processMessage(message); + }, + onStatusUpdate: (status) => { + this.statusUpdateCallback(status); + }, + }); + this.shouldSnap = false; + } +``` + +This constructor takes a single parameter - the `createMapSpecificMarker` function - that is defined as `createMarker` in the `script.js` file - and sets up some defaults. + +Callbacks are provided to the Ably AssetSubscriber constructor to define behaviour for when the drivers status and location are updated. + +In order to connect to Ably, a `connect` function is defined inside the `script.js` file: + +```js + async connect(channelId) { + // First, make sure that if we're already connected to a driver we disconnect from it + + if (this.assetSubscriber.assetConnection) { + await this.assetSubscriber.stop(); + } + + // Now, use the Asset Tracking SDK to connect to the channelId provided in the call to connect + // This defaults to 'ivan', one of our test channel names. + this.assetSubscriber.start(channelId || 'ivan'); + } +``` + +As its name would suggest, `processMessage` processes incoming messages. + +```js + processMessage(message) { + const locationCoordinate = Coordinate.fromMessage(message); + + // The code expects that the incoming message contains a `riderId` in order to do so -`default-id` is used if that property is not found on the inbound message (for the sake of this demo). + const riderId = locationCoordinate.id ?? 'default-id'; + + // When a rider is first detected, a (provider specific) instance of the map marker is created, by calling the factory function provided to the constructor, and is given the coordinates from the message. Then a new instance of the `Vehicle` class is created: + if (!this.rider) { + const marker = this.createMapSpecificMarker(locationCoordinate); + this.rider = new Vehicle(riderId, true, marker); + + marker.focus(); + } + + // The `rider.move` function on the `Vehicle` instance will handle all of the animation and movement of riders across the map UI. + this.rider.move(locationCoordinate, this.shouldSnap); + } +``` + +Next, the `onStatusUpdate()` function is defined at the bottom of the `Vehicle.js` class. + +#### onStatusUpdate + +Finally `onViewerCountUpdated()` is a function provided to allow the `script.js` module to subscribe to the event raised when the Asset Tracking SDK notifies that the asset status has changed: + +```js + onStatusUpdate(callbackFunction) { + this.statusUpdateCallback = callbackFunction; + } +``` + +`RiderConnection` subscribes to, and manages the state of the application. While it calls `rider.move()` every time a new location is detected, it doesn't actually have any code for creating, moving or animating map markers. All of that logic lives inside the `Vehicle.js` class. + +### Vehicle.js - the animation manager + +Vehicle.js looks like this: + +```js +export class Vehicle { + get position() { ... } + async move(destinationCoordinate, snapToLocation = false) { ... } + async animate() { ... } +} +``` + +and is responsible for encapsulating the map marker animations. It uses turf.js to plot a course between its current position and a destination location each time `move()` is called, and most of its work is done inside the animate function. When a `Vehicle` is created, its constructor calls a function called `animate()`. + +The `animate()` function uses the browser's [requestAnimationFrame()](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) API to repeatedly call itself, based on the refresh rate of your browser and your display. + +```js +async animate() { + if (this.moveBuffer.length === 0) { + window.requestAnimationFrame(() => { this.animate(); }); + return; + } + + const targetCoordinate = this.moveBuffer.shift(); + this.marker.updatePosition(targetCoordinate); + + if (this.movementsSinceLastFocused >= this.numberOfMovementsToFocusAfter) { + this.movementsSinceLastFocused = 0; + this.marker.focus(); + } + + window.requestAnimationFrame(() => { this.animate(); }); +} +``` + +It does a few things: + +* It reads data from the `this.moveBuffer`. +* If there's a location in the moveBuffer, it calls `this.marker.updatePosition()` with the target location as a parameter. +* If the number of calls to animate is greater than or equal to the `this.numberOfMovementsToFocusAfter` - a configuration setting that defaults to 100, it pans the map center to the current vehicle location (and then resets `this.movementsSinceLastFocused` to zero). + +The marker adapters are responsible for actually updating the position of the marker on the users screen - for example, the `GoogleMapsMarker` does this: + +```js +updatePosition(targetCoordinate) { + this.marker.setPosition(targetCoordinate); + this.current = targetCoordinate; + ... +} +``` + +using the `Google Maps SDK` `setPosition` function to move the marker, while the `MapBoxMarker` does this: + +```js +updatePosition(targetCoordinate) { + this.marker.setLngLat(targetCoordinate); + this.el.setAttribute('compass-direction', targetCoordinate.compassDirection); +} +``` + +using the `MapBox SDK` `setLngLat()` function to move the marker. + +This animation loop runs over and over, processing any movements that are in the internal `this.moveBuffer` array. If the array is empty, it does nothing. + +In order to animate the marker, the `this.moveBuffer` needs to be filled with coordinates to move between, this is done in the implementation of the `move()` function. + +First, to support the "animation on/off" setting in the UI, there is a `snapToLocation` check at the start of the move function. If `snapToLocation` is enabled, then `this.moveBuffer` is replaced with the final destination of the marker, and the rest of the animation logic is skipped. +This will "jump" the map marker to that location as soon as the `animate()` function is next called. + +```js +async move(destinationCoordinate, snapToLocation = false) { + this.movementsSinceLastFocused++; + + if (snapToLocation) { + this.moveBuffer = [ destinationCoordinate ]; + return; + } +``` + +If the marker needs to animate, the `this.moveBuffer` is reset, and then the current starting location is retrieved from the `this.marker` instance. The Google/MapBox Marker classes will make sure that this marker is returned in a format that the animation logic expects - the start position is then stored in a variable called `currentCoordinate`. + +```js + this.moveBuffer = []; + const currentCoordinate = this.marker.getCurrentCoordinate(); +``` + +Next, using [turf.js](https://turfjs.org/), a path is calculated between `currentCoordinate` and the `destinationCoordinate` that was passed as an argument to `move()` when a message was received by the `Ably channel`. + +```js + var path = turf.lineString([ currentCoordinate.toGeoJson(), destinationCoordinate.toGeoJson() ]); + var pathLength = turf.length(path, { units: 'miles' }); +``` + +This line is then split into 30 steps, each one of these steps representing a single movement in the animation. + +Each computed step is added to the `this.moveBuffer`. + +```js + var numSteps = 30; // This is the FPS + + for (let step = 0; step <= numSteps; step++) { + const curDistance = step / numSteps * pathLength; + const targetLocation = turf.along(path, curDistance, { units: "miles" }); + + const targetCoordinate = Coordinate.fromGeoJson(targetLocation.geometry.coordinates, destinationCoordinate.bearing); + + this.moveBuffer.push(targetCoordinate); + } +} +``` + +The result is that every time the `animate()` function gets called (which on most monitors will be either every 16ms or 33ms), the marker will move the next step down the line. + +### Notes: Create token request / authentication with Ably + +In order to keep this demo as close to a real world application as possible,it uses Ably `tokenAuthentication` to handle the `Ably API key`. This works by providing the client side `Ably SDK` with a url that it can call to get a valid access token. + +In `RiderConnection.js` there's a line that looks like this: + +```js +this.ably = new Ably.Realtime.Promise({ authUrl: "/api/createTokenRequest" }); +``` + +The url "/api/createTokenRequest" - calls a route mapped in the `Express` app found in `/web/server.js` + +```js +const Ably = require('ably/promises'); +require('dotenv').config(); + +const client = new Ably.Realtime(process.env.ABLY_API_KEY); + +app.get("/api/createTokenRequest", async (request, response) => { + const tokenRequestData = await client.auth.createTokenRequest({ + clientId: 'ably-client-side-api-calls-demo' + }); + response.send(tokenRequestData); +}); +``` + +This block of code reads an API key stored in a `.env` file called `ABLY_API_KEY`, which creates an instance of the Ably SDK on the server side, and uses it to send a token to the frontend that can be used without leaking the Ably credentials to anyone with a browser. + +This is the recommended way to handle your Ably API key security in real-world applications. + +[You can read more about this in the docs](https://www.ably.io/documentation/realtime/authentication#token-authentication). + +### Notes: Dealing With Coordinates + +Inside `Coordinates.js` there is a surprisingly large amount of code which formats different JavaScript objects. + +This is because each of the libraries used in this sample - MapBox, GoogleMaps, and turf.js, along with the Ably messages - have a subtly different way of expressing "a latitude and longitude". Sometimes they use "lat", sometimes "latitude", or "long" and "lon", or even as a two value array of [lng,lat]. + +`Coordinate.js` deals with the differences in these formats, converting between them without peppering the code with code copying and renaming properties. + +There are calls to these functions throughout the code - converting coordinates and bearings from one direction to another. + +```js +export class Coordinate { + get latitude() { ... } + get longitude() { ... } + get compassDirection() { ... } + toGeoJson() { ... } + static fromGeoJson(coords, bearing = 0) { ... } + static fromLngLat(lngLatObj, bearing = 0) { ... } + static fromMessage(messageData) { ... } + static bearingToCompass(bearing) { ... } +} +``` + +### Notes: Binding to the UI in Ui.js + +`Ui.js` is called from both of the `script.js` files - this is the code that handles wiring up changes to the values of the animation toggle switch (which is implemented with an HTML checkbox), and handling the button press to change rider channels when a channel name is typed into the HTML text input. + +In a larger application, the UI would probably be taken care of by a UI framework like React or Vue. In order to keep this demo legible and simple,the click handlers are bound by hand using `getElementById`, and have some code in `Ui.js` to update the "Driver is online/offline" message on the screen. + +```js +export function bindUi(riderConnectionInstance) { + + var queryParams = new URLSearchParams(window.location.search); + + const updateChannelButton = document.getElementById("updateChannelButton"); + const channelIdTextBox = document.getElementById("channelID"); + const animationCheckbox = document.getElementById("animation"); + const subscriberCount = document.getElementById("subscriberCount"); + + if (!updateChannelButton || !channelIdTextBox) { + throw new Error("Where has the UI gone? Cannot continue. Can't find ChannelID"); + } + + animationCheckbox.addEventListener("change", (cbEvent) => { + riderConnectionInstance.shouldSnap = !cbEvent.target.checked; + }); + + updateChannelButton.addEventListener("click", () => { + const channelValue = channelIdTextBox.value; + if (channelValue.length > 0) { + riderConnectionInstance.connect(channelIdTextBox.value); + } + }); + + if (queryParams.has("channel")) { + const channelId = queryParams.get("channel"); + channelIdTextBox.value = channelId; + riderConnectionInstance.connect(channelId); + } + + riderConnectionInstance.onStatusUpdate((status) => { updateDriverStatus(status); }); + updateDriverStatus(riderConnectionInstance.driverStatus); +} + + +function updateDriverStatus(status) { + const driverPresent = "Driver is online"; + const noDrivers = "Driver is offline"; + + const message = status ? driverPresent : noDrivers; + subscriberCount.innerHTML = message; +} + +``` From 6d10f11a3ae08db7e5f5e302a7ab6205c6714057 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 17:06:56 +0000 Subject: [PATCH 6/9] use dotenv for google/mapbox api keys --- .../subscribing-example-app/package-lock.json | 108 ++++++++++++++++++ examples/subscribing-example-app/package.json | 1 + .../public/Mapbox/script.js | 5 +- examples/subscribing-example-app/server.js | 19 ++- .../views/{google.html => google.ejs} | 2 +- .../views/{mapbox.html => mapbox.ejs} | 3 + 6 files changed, 131 insertions(+), 7 deletions(-) rename examples/subscribing-example-app/views/{google.html => google.ejs} (92%) rename examples/subscribing-example-app/views/{mapbox.html => mapbox.ejs} (94%) diff --git a/examples/subscribing-example-app/package-lock.json b/examples/subscribing-example-app/package-lock.json index 4f60cd8..d2f70f5 100644 --- a/examples/subscribing-example-app/package-lock.json +++ b/examples/subscribing-example-app/package-lock.json @@ -431,6 +431,14 @@ "uri-js": "^4.2.2" } }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -449,6 +457,11 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -469,6 +482,11 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "base64-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.2.tgz", @@ -508,6 +526,15 @@ "to-utf8": "0.0.1" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -518,6 +545,29 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -526,6 +576,11 @@ "delayed-stream": "~1.0.0" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -604,6 +659,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==", + "requires": { + "jake": "^10.6.1" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -614,6 +677,11 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -676,6 +744,14 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "requires": { + "minimatch": "^3.0.4" + } + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -737,6 +813,11 @@ "har-schema": "^2.0.0" } }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -787,6 +868,17 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -851,6 +943,14 @@ "mime-db": "1.44.0" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1037,6 +1137,14 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, "to-utf8": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", diff --git a/examples/subscribing-example-app/package.json b/examples/subscribing-example-app/package.json index 57a5fce..1df5c7c 100644 --- a/examples/subscribing-example-app/package.json +++ b/examples/subscribing-example-app/package.json @@ -13,6 +13,7 @@ "ably": "^1.2.1", "ably-asset-tracking": "file:../../", "dotenv": "^8.2.0", + "ejs": "^3.1.5", "express": "^4.17.1" }, "engines": { diff --git a/examples/subscribing-example-app/public/Mapbox/script.js b/examples/subscribing-example-app/public/Mapbox/script.js index f47a2aa..6ac90ec 100644 --- a/examples/subscribing-example-app/public/Mapbox/script.js +++ b/examples/subscribing-example-app/public/Mapbox/script.js @@ -3,10 +3,7 @@ import { RiderConnection } from "../RiderConnection.js"; import { Coordinate } from "../Coordinate.js"; import { bindUi } from "../Ui.js"; -(async function() { - - mapboxgl.accessToken = '***REMOVED***'; - +(async function() { const position = new Coordinate(0, 0); const mapElement = "map"; const map = new mapboxgl.Map({ center: position.toGeoJson(), zoom: 15, container: mapElement, style: 'mapbox://styles/mapbox/streets-v11' }); diff --git a/examples/subscribing-example-app/server.js b/examples/subscribing-example-app/server.js index bf4f532..4c4cb08 100644 --- a/examples/subscribing-example-app/server.js +++ b/examples/subscribing-example-app/server.js @@ -11,16 +11,31 @@ const client = new Ably.Realtime(process.env.ABLY_API_KEY); const port = process.env.PORT || 5000; const app = express(); + +app.set('view engine', 'ejs'); + app.get('/', async (request, response) => { response.sendFile(__dirname + '/views/index.html'); }); app.get('/mapbox', async (request, response) => { - response.sendFile(__dirname + '/views/mapbox.html'); + if (!process.env.MAPBOX_API_KEY) { + console.error('MAPBOX_API_KEY environment variable is missing'); + process.exit(1); + } + response.render(__dirname + '/views/mapbox.ejs', { + mapboxApiKey: process.env.MAPBOX_API_KEY, + }); }); app.get('/google', async (request, response) => { - response.sendFile(__dirname + '/views/google.html'); + if (!process.env.GOOGLE_API_KEY) { + console.error('GOOGLE_API_KEY environment variable is missing'); + process.exit(1); + } + response.render(__dirname + '/views/google.ejs', { + googleScriptPath: `https://maps.googleapis.com/maps/api/js?key=${process.env.GOOGLE_API_KEY}&libraries=&v=weekly`, + }); }); app.get('/api/createTokenRequest', async (request, response) => { diff --git a/examples/subscribing-example-app/views/google.html b/examples/subscribing-example-app/views/google.ejs similarity index 92% rename from examples/subscribing-example-app/views/google.html rename to examples/subscribing-example-app/views/google.ejs index 788e000..3101362 100644 --- a/examples/subscribing-example-app/views/google.html +++ b/examples/subscribing-example-app/views/google.ejs @@ -6,7 +6,7 @@ - + diff --git a/examples/subscribing-example-app/views/mapbox.html b/examples/subscribing-example-app/views/mapbox.ejs similarity index 94% rename from examples/subscribing-example-app/views/mapbox.html rename to examples/subscribing-example-app/views/mapbox.ejs index 6f88201..deb890a 100644 --- a/examples/subscribing-example-app/views/mapbox.html +++ b/examples/subscribing-example-app/views/mapbox.ejs @@ -9,6 +9,9 @@ + From 8f82bdb44e7f751d224b77130b7dd2f2a71a6685 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Thu, 10 Dec 2020 17:13:37 +0000 Subject: [PATCH 7/9] update readme to reflect changes to map provider api keys --- examples/subscribing-example-app/README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/subscribing-example-app/README.md b/examples/subscribing-example-app/README.md index 5727f87..feaee39 100644 --- a/examples/subscribing-example-app/README.md +++ b/examples/subscribing-example-app/README.md @@ -4,10 +4,12 @@ This is a demo of using an Ably data stream to place an animating marker on a ma ## To run locally -First configure `TokenRequests` by creating an `.env` file in the root of the web folder and add your [Ably API key](https://support.ably.com/a/solutions/articles/3000030502) as below: +First configure `TokenRequests` by creating an `.env` file in the root of the web folder and add your [Ably API key](https://support.ably.com/a/solutions/articles/3000030502) and Google API Key/Mapbox access token as below: ```bash ABLY_API_KEY=yourably:apikeyhere +GOOGLE_API_KEY=yourgooglemapsapikeyhere +MAPBOX_API_KEY=yourmapboxaccesstokenhere ``` If you are running the app for the first time, then install the dependencies, in the web folder run: @@ -52,7 +54,6 @@ For example, here is the Google `script.js`: ```js (async function() { - const position = new Coordinate(0, 0); // Create start location const mapElement = document.getElementById("map"); // Find mapping element const map = new google.maps.Map(mapElement, { center: position, zoom: 3 }); // Initilise GMaps SDK @@ -76,9 +77,6 @@ and the very similar MapBox `script.js`: ```js (async function() { - - mapboxgl.accessToken = 'access-token-here'; - const position = new Coordinate(0, 0); const mapElement = "map"; const map = new mapboxgl.Map({ center: position.toGeoJson(), zoom: 15, container: mapElement, style: 'mapbox://styles/mapbox/streets-v11' }); From 18bf40e49d83ad54845e57b1f9dc144f8c6ed669 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 11 Dec 2020 11:45:27 +0000 Subject: [PATCH 8/9] allow presenceMessage.data to be json string --- src/lib/AssetConnection.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/AssetConnection.ts b/src/lib/AssetConnection.ts index 37091b7..c6e3e8c 100644 --- a/src/lib/AssetConnection.ts +++ b/src/lib/AssetConnection.ts @@ -99,7 +99,8 @@ class AssetConnection { }; private onPresenceMessage = (presenceMessage: AblyTypes.PresenceMessage) => { - if (presenceMessage.data?.type === ClientTypes.publisher) { + const data = typeof presenceMessage.data === 'string' ? JSON.parse(presenceMessage.data) : presenceMessage.data; + if (data?.type === ClientTypes.publisher) { if (presenceMessage.action === 'enter') { this.notifyAssetIsOnline(); } else if (presenceMessage.action === 'leave') { From aed4bc0f83e88e8b88831b220aaadba471d1a6e8 Mon Sep 17 00:00:00 2001 From: Owen Pearson Date: Fri, 11 Dec 2020 11:48:06 +0000 Subject: [PATCH 9/9] update demo readme to document requirement to build sdk before running --- examples/subscribing-example-app/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/subscribing-example-app/README.md b/examples/subscribing-example-app/README.md index feaee39..4299c51 100644 --- a/examples/subscribing-example-app/README.md +++ b/examples/subscribing-example-app/README.md @@ -17,7 +17,13 @@ If you are running the app for the first time, then install the dependencies, in npm install ``` -Then to run the web demos, in the web folder run: +Also you will need to make sure you build the SDK before you can run the demo app, in the **root directory** of this repository run: +```bash +npm install +npm run build +``` + +Then to run the web demos, in the example app folder run: ```bash npm run start