{"_id":"@blabu.com/timesync","_rev":"2-bb706ba417c0c8b4204ce4e10253da0b","name":"@blabu.com/timesync","dist-tags":{"latest":"1.0.5-alpha.1"},"versions":{"1.0.4":{"name":"@blabu.com/timesync","version":"1.0.4","description":"Time synchronization between peers","author":{"name":"Jos de Jong","email":"wjosdejong@gmail.com","url":"https://github.com/josdejong"},"main":"./lib/timesync.js","license":"MIT","keywords":["time","synchronization","ntp","client","server","isomorphic"],"repository":{"type":"git","url":"git://github.com/enmasseio/timesync.git"},"dependencies":{"debug":"3.1.0"},"devDependencies":{"babel-cli":"6.26.0","babel-preset-es2015":"6.24.1","babelify":"8.0.0","body-parser":"1.18.2","browserify":"16.1.0","express":"4.16.2","promise":"8.0.1","socket.io":"2.0.4","uglify-js":"3.3.10","watch":"1.0.2"},"scripts":{"bundle":"mkdir -p dist; browserify src/timesync.js -t babelify -s timesync -o dist/timesync.js --bare","minify":"uglifyjs dist/timesync.js -o dist/timesync.min.js","compile":"babel -q src/ -d lib/","build":"npm run bundle; npm run minify; npm run compile","watch":"watch 'npm run build' src"},"browserify":{"transform":["babelify"]},"gitHead":"2149c3a3055b747acd2e91b1ddc6f7b0bb962dee","bugs":{"url":"https://github.com/enmasseio/timesync/issues"},"homepage":"https://github.com/enmasseio/timesync#readme","_id":"@blabu.com/timesync@1.0.4","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-s6S8ki6itjB+4eEM4RJmpyXgdFD7NaFDsVytNnZ442+00bYhFr2ZfIKcnaRvBSMZf/q6CgqX+NrV6/lZFV6pqw==","shasum":"771dfb4744e35309e06ee22f263c579e610b01a6","tarball":"https://registry.npmjs.org/@blabu.com/timesync/-/timesync-1.0.4.tgz","fileCount":33,"unpackedSize":59985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNEDbCRA9TVsSAnZWagAAE1IQAIv5o8rPFSmGzqpyhKsS\nsMfEFiwqTPTrL/vKs+tfvSYY7bSmQAFl4GA//R96JphZ1ylf/Pma/ypfleE3\nDpCcsa64IrmzyaZYsF07MBRGjQJF/iqCKzJiBgcv7gDEgjZE91WCI6cJh6bn\nLbyS1fQ//6xAiJHrsum+1WpzTn/k7IXpD6vI643hEm2vex0vkcYZDcE5+Btf\n0IHnTGZez8M80HW/wR3ut1Uv43wc6HEtINu8iprhGorkV3BbmCbkI78Hh+wH\nP4ApxPLN28muUFnABoViM378K+gzo2HQEFph2RRrLRYSU5EIgasmeokphziI\neA6C/3viXSS4wshcp5HPdH1VnBUcSGhiJal21XkM+lTUPiA8+fOoxtM1qm/+\nmCjJCnGT4BMFBljACzlYgXYZq1z6S4a8J7QbCgT0vEKk+GjozvB1b7PxKjOu\npo23T6HdsX+bnlJzcCzb6uqd4Z2fcoRGWmRzfKFn7xykNk3fteKZyZ4Xm5QH\nnkiAw/QcQG1Mz1srwSozmzdiMQFoIv/RggjsWUmxcL3VeXQ6lB7dRT18lJHN\nADx8tnwD6/7AjWlMkQU2Px8JeFpAau0aMXTcReJ4MkHdy59W99nZcgo5esoF\nDSD16660CnQGzyEOLFtzK+NO7BXpmh7QnXVNMlIyiAwsgRXxB+/8f6W43Twm\nBddt\r\n=xPEM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGL3XY3MuH12wTmiMJFrJZhEx0Sj0QKWp1kF4sCCKNRYAiA1EYz5PJW0XdhSii48jMW9sf07vs+RImJPywQFE8Y7sA=="}]},"maintainers":[{"name":"cernytomas","email":"cernytomasj@gmail.com"},{"name":"poky85","email":"mail@jiripokorny.cz"},{"name":"lukaskaras","email":"lukaskaras.lk@gmail.com"},{"name":"t.voslar","email":"t.voslar@gmail.com"},{"name":"tomasstrejcek","email":"tomas.strejcek@ghn.cz"}],"_npmUser":{"name":"poky85","email":"mail@jiripokorny.cz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/timesync_1.0.4_1580482778734_0.8420984630671495"},"_hasShrinkwrap":false},"1.0.5-alpha.1":{"name":"@blabu.com/timesync","version":"1.0.5-alpha.1","description":"Time synchronization between peers","author":{"name":"Jos de Jong","email":"wjosdejong@gmail.com","url":"https://github.com/josdejong"},"main":"./lib/timesync.js","license":"MIT","keywords":["time","synchronization","ntp","client","server","isomorphic"],"repository":{"type":"git","url":"git://github.com/enmasseio/timesync.git"},"dependencies":{"debug":"3.1.0"},"devDependencies":{"babel-cli":"6.26.0","babel-preset-es2015":"6.24.1","babelify":"8.0.0","body-parser":"1.18.2","browserify":"16.1.0","express":"4.16.2","promise":"8.0.1","socket.io":"2.0.4","uglify-js":"3.3.10","watch":"1.0.2"},"scripts":{"bundle":"mkdir -p dist; browserify src/timesync.js -t babelify -s timesync -o dist/timesync.js --bare","minify":"uglifyjs dist/timesync.js -o dist/timesync.min.js","compile":"babel -q src/ -d lib/","build":"npm run bundle; npm run minify; npm run compile","watch":"watch 'npm run build' src"},"browserify":{"transform":["babelify"]},"gitHead":"2149c3a3055b747acd2e91b1ddc6f7b0bb962dee","bugs":{"url":"https://github.com/enmasseio/timesync/issues"},"homepage":"https://github.com/enmasseio/timesync#readme","_id":"@blabu.com/timesync@1.0.5-alpha.1","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-OynGLDi/gfoh6xysVhyPCmCDwNSjbdj7srb7A/OzK9vis5C8MIuaq/fq+FGCefC7f+d1iPe1qMA3H5Iga0shgg==","shasum":"dc87981186caed246bbc479d5ddfdf2e808d1a2d","tarball":"https://registry.npmjs.org/@blabu.com/timesync/-/timesync-1.0.5-alpha.1.tgz","fileCount":42,"unpackedSize":122762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNESlCRA9TVsSAnZWagAARjkP/2yP/s40h2qq2tz8lH7m\nieV5zt75G3Nit4uhQ8bwpekMvQJs1UKGb3IK15395jyyXzIhXqxtGL8GUOHf\na2PJms4TBP9crC066c6vgpcH9HYkSFVzbE9gvgjOLe30wT/XH2xCf1G2GN53\nOUTdi++bmv+iKwaNurGTWCrPbMRsv/+Pc2xajb8ZZR9/Y1BXz5nNwrQfn4gy\nmSpCqlSGf4aUDl9cumVAkyAnXi/FNrYjE/8MsbTMyw2dqT0CPqyGXHgxSLf0\nHEcoxx9cCi+rDx8WsMLIWvLd2FjjZiTRogGUedJ1tFLBtO1j8wxDFJAK6LZA\n7Qz94ZTS7OVVfGu1inOtGPMv7aXZGUjYPg4ZN+5HA0ENixpdmBnv2IuLxJvV\nu74vR4fFZ+dibMoGTgb91TOUnr9hju7Ec5w8P7sKEbO1NTYdiunLY4m99fxB\nSKdkUjWjBQUWOe71bdJItTYnjyQ7Hw+8kxYoflnUIT6fJXthMtw4nYX+Ks+q\nR9YZmniSR7qZyzUWmoue7COS28tYrJ/YGbv32zNePD0eZ9CKYUWsOAIen1Gn\noa68NdRK0iJAWDekg96srmEc0ztMNXqQSq0lxoU5ha9XXs+h7rdFSzvhvtws\nGFKFhqeglzvjztreLcfe1Tx1b6lQgPvGfus93mvlg3QjGSic9ZRISFV9yeFN\nV84t\r\n=K7+w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcDJ7rDae5aYIibHWthgyaomVbvRZQ8RRKs8tzODDTVwIhAOg3s5ht7DG3ByKW/Xdx/EzcKzJcG9q9tBgtrdAuIoIp"}]},"maintainers":[{"name":"cernytomas","email":"cernytomasj@gmail.com"},{"name":"lukaskaras","email":"lukaskaras.lk@gmail.com"},{"name":"poky85","email":"mail@jiripokorny.cz"},{"name":"t.voslar","email":"t.voslar@gmail.com"},{"name":"tomasstrejcek","email":"tomas.strejcek@ghn.cz"}],"_npmUser":{"name":"poky85","email":"mail@jiripokorny.cz"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/timesync_1.0.5-alpha.1_1580483748559_0.6955821917294085"},"_hasShrinkwrap":false}},"time":{"created":"2020-01-31T14:59:38.552Z","1.0.4":"2020-01-31T14:59:38.842Z","modified":"2022-04-04T19:19:17.273Z","1.0.5-alpha.1":"2020-01-31T15:15:48.656Z"},"maintainers":[{"name":"cernytomas","email":"cernytomasj@gmail.com"},{"name":"lukaskaras","email":"lukaskaras.lk@gmail.com"},{"name":"poky85","email":"mail@jiripokorny.cz"},{"name":"t.voslar","email":"t.voslar@gmail.com"},{"name":"tomasstrejcek","email":"tomas.strejcek@ghn.cz"}],"description":"Time synchronization between peers","homepage":"https://github.com/enmasseio/timesync#readme","keywords":["time","synchronization","ntp","client","server","isomorphic"],"repository":{"type":"git","url":"git://github.com/enmasseio/timesync.git"},"author":{"name":"Jos de Jong","email":"wjosdejong@gmail.com","url":"https://github.com/josdejong"},"bugs":{"url":"https://github.com/enmasseio/timesync/issues"},"license":"MIT","readme":"# timesync\n\nTime synchronization between peers.\n\nUsage scenarios:\n\n- **master/slave**: Clients synchronize their time to that of a single server,\n  via either HTTP requests or WebSockets.\n- **peer-to-peer**: Clients are connected in a (dynamic) peer-to-peer network\n  using WebRTC or WebSockets and must converge to a single, common time in the\n  network.\n\n\n# Install\n\nInstall via npm:\n\n```\nnpm install @blabu.com/timesync\n```\n\n\n# Usage\n\nA timesync client can basically connect to one server or multiple peers,\nand will synchronize it's time. The synchronized time can be retrieved via\nthe method `now()`, and the client can subscribe to events like `'change'`\nand `'sync'`.\n\n```js\n// create a timesync instance\nvar ts = timesync({\n  server: '...',  // either a single server,\n  peers: [...]    // or multiple peers\n});\n\n// get notified on changes in the offset\nts.on('change', function (offset) {\n  console.log('offset from system time:', offset, 'ms');\n}\n\n// get the synchronized time\nconsole.log('now:', new Date(ts.now()));\n```\n\n\n# Example\n\nHere a full usage example with express.js, showing both server and client side.\n`timesync` has build-in support for requests over http and can be used with\nexpress, a default http server, or other solutions. `timesync` can also be\nused over other transports than http, for example using websockets or webrtc.\nThis is demonstrated in the [advanced examples](/examples/advanced).\n\nMore examples are available in the [/examples](/examples) folder.\nSome of the examples use libraries like `express` or `socket.io`.\nBefore you can run these examples you will have to install these dependencies.\n\n**server.js**\n\n```js\nvar express = require('express');\nvar timesyncServer = require('timesync/server');\n\n// create an express app\nvar port = 8081;\nvar app = express();\napp.listen(port);\nconsole.log('Server listening at http://localhost:' + port);\n\n// serve static index.html\napp.get('/', express.static(__dirname));\n\n// handle timesync requests\napp.use('/timesync', timesyncServer.requestHandler);\n```\n\n**index.html**\n\n```html\n<!DOCTYPE html>\n<html>\n<head>\n  <!-- note: for support on older browsers, you will need to load es5-shim and es6-shim -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.0.5/es5-shim.min.js\"></script>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.23.0/es6-shim.min.js\"></script>\n\n  <script src=\"/timesync/timesync.js\"></script>\n</head>\n<script>\n  // create a timesync instance\n  var ts = timesync.create({\n    server: '/timesync',\n    interval: 10000\n  });\n\n  // get notified on changes in the offset\n  ts.on('change', function (offset) {\n    document.write('changed offset: ' + offset + ' ms<br>');\n  });\n\n  // get synchronized time\n  setInterval(function () {\n    var now = new Date(ts.now());\n    document.write('now: ' + now.toISOString() + ' ms<br>');\n  }, 1000);\n</script>\n</html>\n```\n\n\n# API\n\n## Client\n\n### Construction\n\nAn instance of timesync is created as:\n\n```js\nvar ts = timesync(options);\n```\n\n#### Options\n\nThe following options are available:\n\nName       | Type                   | Default    | Description\n---------- | ---------------------- | ---------- | ----------------------------------------\n`delay`    | `number`               | `1000`     | Delay in milliseconds between every request sent.\n`interval` | `number` or `null`     | `3600000`  | Interval in milliseconds for running a synchronization. Defaults to 1 hour. Set to `null` to disable automatically running synchronizations (synchronize by calling `sync()`).\n`now`      | `function`             | `Date.now` | Function returning the local system time.\n`peers`    | `string[]` or `string` | `[]`       | Array or comma separated string with uri's or id's of the peers to synchronize with. Cannot be used in conjunction with option `server`.\n`repeat`   | `number`               | `5`        | Number of times to do a request to every peer.\n`server`   | `string`               | none       | Url of a single server in case of a master/slave configuration. Cannot be used in conjunction with option `peers`.\n`timeout`  | `number`               | `10000`    | Timeout in milliseconds for requests to fail.\n\n### Methods\n\nName                  | Return type | Description\n--------------------- | ----------- | ----------------------------------\n`destroy()`           | none        | Destroy the timesync instance. Stops automatic synchronization. If timesync is currently executing a synchronization, this synchronization will be finished first.\n`now()`               | `number`    | Get the synchronized time. Returns a timestamp. To create a `Date`, call `new Date(time.now())`.\n`on(event, callback)` | `Object`    | Register a callback handler for an event. Returns the timesync instance. See section [Events](#events) for more information.\n`off(event [, callback])` | `Object`    | Unregister a callback handler for an event. If no callback is provided, all callbacks of this event will be removed. Returns the timesync instance. See section [Events](#events) for more information.\n`sync()`  | none        | Do a synchronization with all peers now.\n\nTo be able to send and receive messages from peers, `timesync` needs a transport. To hook up a transport like a websocket or http requests, one has to override the `send(id, data)` method of the `timesync` instance, and has to call `ts.receive(id, data)` on incoming messages.\n\nName                                | Return type | Description\n----------------------------------- | ----------- | ----------------------------------\n`send(to, data, timeout) : Promise` | none        | Send a message to a peer. `to` is the id of the peer, and `data` a JSON object containing the message. Must return a Promise which resolves when the message has been sent, or rejects when sending failed or a timeout occurred.\n`receive(from, data)`               | none        | Receive a message from a peer. `from` is the id of the sender, and `data` a JSON object containing the message.\n\n`timesync` sends messages using the JSON-RPC protocol, as described in the section [Protocol](#protocol).\n\n\n### Events\n\n`timesync` emits events when starting and finishing a synchronization, and when the time offset changes. To listen for events:\n\n```js\nts.on('change', function (offset) {\n  console.log('offset changed:', offset);\n});\n```\n\nAvailable events:\n\nName     | Description\n---------| ----------\n`change` | Emitted when the offset is changed. This can only happen during a synchronization. Callbacks are called with the new offset (a number) as argument.\n`error`  | Emitted when an error occurred. Callbacks are called with the error as argument.\n`sync`   | Emitted when a synchronization is started or finished. Callback are called with a value `'start'` or `'end'` as argument.\n\n\n### Properties\n\nName      | Type     | Description\n--------- | -------- | --------------------------------------------\n`offset`  | `number` | The offset from system time in milliseconds.\n`options` | `Object` | An object holding all options of the timesync instance. One can safely adjust options like `peers` at any time. Not all options can be changed after construction, for example a changed `interval` value will not be applied.\n\n\n## Server\n\n`timesync` comes with a build in server to serve as a master for time synchronization. Clients can adjust their time to that of the server. The server basically just implements a POST request responding with its current time, and serves the static files `timesync.js` and `timesync.min.js` from the `/dist` folder. It's quite easy to implement this request handler yourself, as is demonstrated in the [advanced examples](/examples/advanced).\n\nThe protocol used by the server is described in the section [Protocol](#protocol).\n\n### Load\n\nThe server can be loaded in node.js as:\n\n```js\nvar timesyncServer = require('timesync/server');\n```\n\n### Methods\n\nName                          | Return type  | Description\n----------------------------- | ------------ | ----------------------------------\n`createServer()`              | `http.Server`| Create a new, dedicated http Server. This is just a shortcut for doing `http.createServer( timesyncServer.requestHandler )`.\n`attachServer(server, [path])`| `http.Server`| Attach a request handler for time synchronization requests to an existing http Server. Argument `server` must be an instance of `http.Server`. Argument `path` is optional, and is `/timesync` by default.\n\n\n### Properties\n\nName              | Type       | Description\n----------------- | ---------- | --------------------------------------------\n`requestHandler`  | `function` | A default request handler, handling requests for the timesync server. Signature is `requestHandler(request, response)`. This handler can be used to attach to an expressjs server, or to create a plain http server by doing `http.createServer( timesyncServer.requestHandler )`.\n\n\n# Protocol\n\n`timesync` sends messages using the JSON-RPC protocol. A peer sends a message:\n\n```json\n{\"jsonrpc\": \"2.0\", \"id\": \"12345\", \"method\": \"timesync\"}\n```\n\nThe receiving peer replies with the same id and its current time:\n\n```json\n{\"jsonrpc\": \"2.0\", \"id\": \"12345\", \"result\": 1423151204595}\n```\n\nThe sending peer matches the returned message by id and uses the result to adjust it's offset.\n\n\n# Algorithm\n\n`timesync` uses a simple synchronization protocol aimed at the gaming industry, and extends this for peer-to-peer networks. The algorithm is described [here](http://www.mine-control.com/zack/timesync/timesync.html):\n\n> A simple algorithm with these properties is as follows:\n>\n> 1. Client stamps current local time on a \"time request\" packet and sends to server\n> 2. Upon receipt by server, server stamps server-time and returns\n> 3. Upon receipt by client, client subtracts current time from sent time and divides by two to compute latency. It subtracts current time from server time to determine client-server time delta and adds in the half-latency to get the correct clock delta. (So far this algorithm is very similar to SNTP)\n> 4. The first result should immediately be used to update the clock since it will get the local clock into at least the right ballpark (at least the right timezone!)\n> 5. The client repeats steps 1 through 3 five or more times, pausing a few seconds each time. Other traffic may be allowed in the interim, but should be minimized for best results\n> 6. The results of the packet receipts are accumulated and sorted in lowest-latency to highest-latency order. The median latency is determined by picking the mid-point sample from this ordered list.\n> 7. All samples above approximately 1 standard-deviation from the median are discarded and the remaining samples are averaged using an arithmetic mean.\n\nThis algorithm assumes multiple clients synchronizing with a single server. In case of multiple peers, `timesync` will take the average offset of all peers (excluding itself) as offset.\n\n\n# Tutorials\n\n- [Using the timesync library in Android applications](https://github.com/enmasseio/timesync/blob/master/docs/android-tutorial.md)\n\n\n# Resources\n\n- [A Stream-based Time Synchronization Technique For Networked Computer Games](http://www.mine-control.com/zack/timesync/timesync.html)\n- [Network Time Protocol](http://www.wikiwand.com/en/Network_Time_Protocol)\n\n\n# Build\n\nTo build the library:\n\n    npm install\n    npm run build\n\nThis will generate the files `timesync.js` and `timesync.min.js` in the folder `/dist`.\n\nTo automatically build on changes, run:\n\n    npm run watch\n","readmeFilename":"README.md"}