mirror of
https://github.com/Wessel/Snowflakey.git
synced 2026-06-08 14:19:02 +02:00
Rewrite into TypeScript
This commit is contained in:
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = false
|
||||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
65
.eslintrc
Normal file
65
.eslintrc
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"env": {
|
||||||
|
"es6": true,
|
||||||
|
"amd": true,
|
||||||
|
"node": true,
|
||||||
|
"mongo": true,
|
||||||
|
"jquery": true,
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 9,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true,
|
||||||
|
"forOf": true,
|
||||||
|
"spread": true,
|
||||||
|
"modules": true,
|
||||||
|
"classes": true,
|
||||||
|
"generators": true,
|
||||||
|
"restParams": true,
|
||||||
|
"regexUFlag": true,
|
||||||
|
"regexYFlag": true,
|
||||||
|
"globalReturn": true,
|
||||||
|
"destructuring": true,
|
||||||
|
"impliedStrict": true,
|
||||||
|
"blockBindings": true,
|
||||||
|
"defaultParams": true,
|
||||||
|
"octalLiterals": true,
|
||||||
|
"arrowFunctions": true,
|
||||||
|
"binaryLiterals": true,
|
||||||
|
"templateStrings": true,
|
||||||
|
"superInFunctions": true,
|
||||||
|
"unicodeCodePointEscapes": true,
|
||||||
|
"objectLiteralShorthandMethods": true,
|
||||||
|
"objectLiteralComputedProperties": true,
|
||||||
|
"objectLiteralDuplicateProperties": true,
|
||||||
|
"objectLiteralShorthandProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": [],
|
||||||
|
"rules": {
|
||||||
|
"semi": "warn",
|
||||||
|
"indent": [ 0, 2 ],
|
||||||
|
"strict": "off",
|
||||||
|
"eqeqeq": "error",
|
||||||
|
"no-var": "warn",
|
||||||
|
"no-undef": "warn",
|
||||||
|
"valid-jsdoc": "warn",
|
||||||
|
"comma-dangle": "warn",
|
||||||
|
"no-dupe-args": "warn",
|
||||||
|
"no-dupe-keys": "warn",
|
||||||
|
"require-await": "warn",
|
||||||
|
"spaced-comment": "error",
|
||||||
|
"space-in-parens": "error",
|
||||||
|
"no-global-assign": "warn",
|
||||||
|
"no-duplicate-imports": "error",
|
||||||
|
"no-dupe-class-members": "error"
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"_config": false,
|
||||||
|
"console": false
|
||||||
|
}
|
||||||
|
}
|
||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
14
.github/CONTRIBUTING.md
vendored
Normal file
14
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
**The issue tracker is only for bug reports and enhancement suggestions. If you have a question, please ask it in the [Discord](https://discord.gg/SV7DAE9) instead of opening an issue – you will get redirected there anyway.**
|
||||||
|
|
||||||
|
If you wish to contribute to Aimaina, feel free to fork the repository and submit a pull request.
|
||||||
|
[ESLint](https://eslint.org/) is used to correct most typo's you make, so it would be helpful if you added [ESLint](https://eslint.org/) to your editor of choice)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
To get ready to work on the codebase, please do the following:
|
||||||
|
|
||||||
|
1. Fork & clone the repository, and make sure you're on the **master** branch
|
||||||
|
2. Run `yarn --dev` or `npm install --dev`
|
||||||
|
4. Code your heart out and test using `yarn test` or `npm run test`!
|
||||||
|
6. [Submit a pull request](https://github.com/PassTheWessel/aimaina/compare)
|
||||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
patreon: wessel
|
||||||
|
ko_fi: wessel
|
||||||
26
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Bug Report
|
||||||
|
about: Report an issue with Aimaina
|
||||||
|
title: "[BUG] Somenthing broke"
|
||||||
|
labels: bug
|
||||||
|
assignees: PassTheWessel
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Please describe the problem you are having in as much detail as possible:**
|
||||||
|
|
||||||
|
|
||||||
|
**Include a reproducible code sample here, if possible:**
|
||||||
|
```js
|
||||||
|
// Place your code here
|
||||||
|
```
|
||||||
|
|
||||||
|
**Further details:**
|
||||||
|
- Node.js version:
|
||||||
|
- Operating system:
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If this applies to you, please check the respective checkbox: [ ] becomes [x].
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] I have also tested the issue on latest master, commit hash:
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Request a feature for Aimaina
|
||||||
|
title: "[ENHANCEMENT] This is an amazing new idea!"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: PassTheWessel
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the ideal solution**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
**Please describe the changes this PR makes and why it should be merged:**
|
||||||
|
|
||||||
|
|
||||||
|
**Status**
|
||||||
|
- [ ] Code changes have been tested and there aren't any typos in it
|
||||||
|
|
||||||
|
**Semantic versioning classification:**
|
||||||
|
- [ ] This PR changes wumpfetch's core codebase (methods or parameters added)
|
||||||
|
- [ ] This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
|
||||||
|
- [ ] This PR **only** includes non-code changes, like changes to documentation, README, etc.
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
89
.vscode/settings.json
vendored
Normal file
89
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
General vscode settings for all my projects
|
||||||
|
Made by Wessel "wesselgame" T <discord@go2it.eu> (https://github.com/PassTheWessel)
|
||||||
|
|
||||||
|
Extensions: rainglow, eslint, tslint, material icon theme, gitlens, fish-vscode, Docker,
|
||||||
|
Popping and Locking theme, Sass, stylelint, SVG viewer, Trailing spaces, Auto renaming tags
|
||||||
|
|
||||||
|
Color themes: Banner (rainglow), Hyrule (rainglow), Azure (rainglow), Github (rainglow),
|
||||||
|
Heroku (rainglow), Popping and Locking
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
// Window
|
||||||
|
"window.zoomLevel": 0,
|
||||||
|
// Editor
|
||||||
|
"editor.fontSize": 13,
|
||||||
|
"editor.fontWeight": "200",
|
||||||
|
"editor.fontFamily": "'SFMono-Regular','Consolas','Liberation Mono','Menlo','Courier','monospace'",
|
||||||
|
"editor.lineHeight": 20,
|
||||||
|
"editor.fontLigatures": true,
|
||||||
|
"editor.cursorStyle": "line",
|
||||||
|
"editor.cursorWidth": 0,
|
||||||
|
"editor.cursorBlinking": "blink",
|
||||||
|
"editor.multiCursorModifier": "ctrlCmd",
|
||||||
|
"editor.minimap.enabled": true,
|
||||||
|
"editor.smoothScrolling": true,
|
||||||
|
"editor.minimap.renderCharacters": true,
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.autoIndent": false,
|
||||||
|
"editor.insertSpaces": true,
|
||||||
|
"editor.tabCompletion": "on",
|
||||||
|
"editor.formatOnPaste": true,
|
||||||
|
"editor.detectIndentation": false,
|
||||||
|
"editor.wordWrap": "off",
|
||||||
|
"editor.matchBrackets": true,
|
||||||
|
"editor.renderWhitespace": "none",
|
||||||
|
"editor.autoClosingBrackets": "always",
|
||||||
|
"editor.tokenColorCustomizations": {
|
||||||
|
"types": "#C59F61",
|
||||||
|
"strings": "#F6EB90",
|
||||||
|
"numbers": "#C54121",
|
||||||
|
"keywords": "#C59F49",
|
||||||
|
"comments": "#6a737d",
|
||||||
|
"variables": "#C55F45",
|
||||||
|
"functions": "#C59F55"
|
||||||
|
},
|
||||||
|
//Workbench
|
||||||
|
"workbench.iconTheme": "material-icon-theme",
|
||||||
|
"workbench.colorTheme": "Banner (rainglow)",
|
||||||
|
"workbench.editor.showTabs": true,
|
||||||
|
"workbench.editor.tabSizing": "fit",
|
||||||
|
"workbench.sideBar.location": "left",
|
||||||
|
// Debug
|
||||||
|
"debug.toolBarLocation": "floating",
|
||||||
|
"debug.allowBreakpointsEverywhere": true,
|
||||||
|
// Console
|
||||||
|
"terminal.integrated.shell.windows": "C:\\Windows\\System32\\bash.exe", // C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe
|
||||||
|
// Files
|
||||||
|
"files.autoSave": "off",
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.git": false,
|
||||||
|
"**/.svn": false,
|
||||||
|
"**/.hg": false,
|
||||||
|
"**/CVS": false,
|
||||||
|
"**/.DS_Store": false
|
||||||
|
},
|
||||||
|
// Breadcrumbs
|
||||||
|
"breadcrumbs.enabled": true,
|
||||||
|
"breadcrumbs.filePath": "last",
|
||||||
|
"breadcrumbs.symbolPath": "on",
|
||||||
|
"breadcrumbs.symbolSortOrder": "name",
|
||||||
|
// Git
|
||||||
|
"git.enableSmartCommit": true,
|
||||||
|
"git.ignoreLimitWarning": true,
|
||||||
|
// ESlint
|
||||||
|
"eslint.enable": true,
|
||||||
|
"eslint.packageManager": "yarn",
|
||||||
|
// Languages
|
||||||
|
"[yaml]": {
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
"editor.autoIndent": false,
|
||||||
|
"editor.insertSpaces": true
|
||||||
|
},
|
||||||
|
"[markdown]": {
|
||||||
|
"editor.wordWrap": "on",
|
||||||
|
"editor.quickSuggestions": false
|
||||||
|
},
|
||||||
|
// Live share
|
||||||
|
"liveshare.featureSet": "insiders",
|
||||||
|
}
|
||||||
18
.vscode/snippets.code-snippets
vendored
Normal file
18
.vscode/snippets.code-snippets
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
// Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||||
|
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||||
|
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||||
|
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||||
|
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||||
|
// Placeholders with the same ids are connected.
|
||||||
|
// Example:
|
||||||
|
// "Print to console": {
|
||||||
|
// "scope": "javascript,typescript",
|
||||||
|
// "prefix": "log",
|
||||||
|
// "body": [
|
||||||
|
// "console.log('$1');",
|
||||||
|
// "$2"
|
||||||
|
// ],
|
||||||
|
// "description": "Log output to console"
|
||||||
|
// }
|
||||||
|
}
|
||||||
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* @PassTheWessel
|
||||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at discord@go2it.eu. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
2
LICENSE
2
LICENSE
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019-present Wessel "wesselgame" T
|
Copyright (c) 2019-present Wessel "wesselgame" T <discord@go2it.eu>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
56
README.md
56
README.md
@@ -5,38 +5,58 @@
|
|||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
```sh
|
```sh
|
||||||
$ yarn add snowflakey # Install w/ Yarn (the superior package manager)
|
$ yarn add snowflakey # Install w/ Yarn
|
||||||
$ npm i snowflakey # Install w/ NPM
|
$ npm i snowflakey # Install w/ NPM
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
##### Code
|
##### Using a worker
|
||||||
```js
|
```js
|
||||||
// require & generate the instance
|
// Declare snowflakey
|
||||||
const Snowflake = require( './generator' );
|
const snowflakey = require('snowflakey');
|
||||||
const snowflake = new Snowflake.generator({
|
// Create the worker instance
|
||||||
processBits: 0,
|
const Worker = new snowflakey.Worker({
|
||||||
|
name: 'starling',
|
||||||
|
epoch: 1420070400000,
|
||||||
|
workerId: process.env.CLUSTER_ID || 31,
|
||||||
|
processId: process.pid || undefined,
|
||||||
workerBits: 8,
|
workerBits: 8,
|
||||||
incrementBits: 14,
|
processBits: 0,
|
||||||
workerId: process.env.CLUSTER_ID || 31
|
incrementBits: 14
|
||||||
});
|
});
|
||||||
|
|
||||||
// exports for global use
|
// Generate the snowflake
|
||||||
exports.makeSnowflake = ( date ) => { return snowflake._generate( date ); };
|
const flake = Worker.generate();
|
||||||
exports.unmakeSnowflake = ( flake ) => { let decon = snowflake.deconstruct( flake ); return decon.timestamp.valueOf(); };
|
console.log(`Created snowflake: ${flake}`);
|
||||||
|
console.log(`Creation date : ${snowflakey.lookup(flake, worker.options.epoch)}`);
|
||||||
|
console.log(`Deconstructed : ${Worker.deconstruct(flake).timestamp.valueOf()}`);
|
||||||
|
```
|
||||||
|
|
||||||
// example
|
##### Using a master
|
||||||
const flake = this.makeSnowflake( Date.now() );
|
```js
|
||||||
console.log( flake );
|
// ... Worker code
|
||||||
console.log( `Creation date: ${Snowflake.lookup( flake, 1420070400000 )}` );
|
// Create the master instance and add the worker
|
||||||
console.log( this.unmakeSnowflake( flake ) );
|
const Master = new snowflakey.Master();
|
||||||
|
master.addWorker(Worker);
|
||||||
|
// Listen to the events
|
||||||
|
master.on('newSnowflake', (data) => {
|
||||||
|
console.log(`created snowflake: ${data.snowflake} by Worker ${data.worker.options.name || data.worker.options.workerId}`)
|
||||||
|
console.log(`Creation date : ${Snowflake.lookup(flake, data.worker.options.epoch)}`);
|
||||||
|
data.worker.deconstruct(data.snowflake);
|
||||||
|
});
|
||||||
|
|
||||||
|
master.on('deconstructedFlake', (data) => {
|
||||||
|
console.log(`Deconstructed : ${data.timestamp.valueOf()} by Worker ${data.worker.options.name || data.worker.options.workerId}`);
|
||||||
|
});
|
||||||
|
// Make the worker generate a snowflake
|
||||||
|
worker.generate();
|
||||||
```
|
```
|
||||||
##### Result
|
##### Result
|
||||||
```sh
|
```sh
|
||||||
$ node test.js
|
$ node test.js
|
||||||
534760094454759424
|
Created snowflake: 534760094454759424
|
||||||
Creation date : 2019-1-15 16:45:41
|
Creation date : 2019-1-15 16:45:41
|
||||||
1547567141880
|
Deconstructed : 1547567141880
|
||||||
```
|
```
|
||||||
|
|
||||||
### What is a snowflake?
|
### What is a snowflake?
|
||||||
|
|||||||
129
generator.js
129
generator.js
@@ -1,129 +0,0 @@
|
|||||||
const big = require( './fake_node_modules/bigInt' );
|
|
||||||
|
|
||||||
const sleep = ( time = 1 ) => { return new Promise( res => { setTimeout( res, time ); } ); };
|
|
||||||
const getBits = ( bits ) => { return ( 2 ** bits ) - 1; };
|
|
||||||
|
|
||||||
exports.lookup = ( flake = 0, epoch = 1420070400000 ) => { return new Date( ( flake / 4194304 ) + epoch ).toLocaleString(); };
|
|
||||||
exports.generator = class Snowflake {
|
|
||||||
constructor( options = {} ) {
|
|
||||||
this.options = Object.assign({
|
|
||||||
async : false,
|
|
||||||
epoch : 1420070400000,
|
|
||||||
workerId : 0,
|
|
||||||
processId : 0,
|
|
||||||
stringify : true,
|
|
||||||
workerBits : 5,
|
|
||||||
processBits : 5,
|
|
||||||
incrementBits: 12
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
// an object containing mutable (unfrozen) properties
|
|
||||||
this.mutable = {
|
|
||||||
locks : [],
|
|
||||||
locked : false,
|
|
||||||
increment : big.zero.subtract( 1 ),
|
|
||||||
lastTimestamp: Date.now()
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( this.options.incrementBits + this.options.processBits + this.options.workerBits !== 22) throw new Error( 'incrementBits, processBits, and workerBits must add up to 22.' );
|
|
||||||
|
|
||||||
// ensure that ids conform to the number of bits
|
|
||||||
this.options.workerId = this.options.workerId % ( 2 ** this.options.workerBits );
|
|
||||||
this.options.processId = this.options.processId % ( 2 ** this.options.processBits );
|
|
||||||
|
|
||||||
// check if NaN
|
|
||||||
if ( isNaN( this.options.workerId ) ) this.options.workerId = 0;
|
|
||||||
if ( isNaN( this.options.processId ) ) this.options.processId = 0;
|
|
||||||
|
|
||||||
// store the maximum increment bound
|
|
||||||
this.maxIncrement = 2 ** this.options.incrementBits;
|
|
||||||
|
|
||||||
// calculate the shifted worker/process ids for later reference
|
|
||||||
this.workerId = big( this.options.workerId ).shiftLeft( this.options.incrementBits + this.options.processBits );
|
|
||||||
this.processId = big( this.options.processId ).shiftLeft( this.options.incrementBits );
|
|
||||||
|
|
||||||
// freeze options and this object, to prevent tampering
|
|
||||||
Object.freeze( this.options );
|
|
||||||
Object.freeze( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
get increment() { return this.mutable.increment = this.mutable.increment.next().mod( this.maxIncrement ); }
|
|
||||||
|
|
||||||
generate() {
|
|
||||||
if ( this.options.async ) return this._generateAsync();
|
|
||||||
else return this._generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
_generate( date, increment = null ) {
|
|
||||||
// 0000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000
|
|
||||||
let flake = big( date || Date.now() ).minus( this.options.epoch ).shiftLeft( 22 )
|
|
||||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbb00000000000000000
|
|
||||||
.add( this.workerId )
|
|
||||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbccccc000000000000
|
|
||||||
.add( this.processId )
|
|
||||||
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbcccccdddddddddddd
|
|
||||||
.add( increment || this.increment );
|
|
||||||
|
|
||||||
if ( this.options.stringify ) flake = flake.toString();
|
|
||||||
|
|
||||||
return flake;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lock() {
|
|
||||||
if ( this.mutable.locked ) return new Promise( res => this.mutable.locks.push( res ) );
|
|
||||||
else this.mutable.locked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_unlock() {
|
|
||||||
if ( this.mutable.locks.length > 0 ) this.mutable.locks.shift()();
|
|
||||||
else this.mutable.locked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _generateAsync() {
|
|
||||||
let lock = this._lock();
|
|
||||||
if( lock ) await lock;
|
|
||||||
let now = Date.now();
|
|
||||||
// check if increment should be reset
|
|
||||||
if ( this.mutable.lastTimestamp !== now ) {
|
|
||||||
// last timestamp didnt match, reset increment
|
|
||||||
this.mutable.increment = big.zero;
|
|
||||||
this.mutable.lastTimestamp = now;
|
|
||||||
} else {
|
|
||||||
// last timestamp matched, increase increment
|
|
||||||
this.mutable.increment = this.mutable.increment.next();
|
|
||||||
// check if increment exceeds max bounds
|
|
||||||
if ( this.mutable.increment.greaterOrEquals( this.maxIncrement ) ) {
|
|
||||||
// sleep for 2ms - 1ms has a risk of timestamp not incrementing for some reason?
|
|
||||||
await sleep( 2 );
|
|
||||||
// reset increment
|
|
||||||
this.mutable.increment = big.zero;
|
|
||||||
now = this.mutable.lastTimestamp = Date.now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate a snowflake with the new increment
|
|
||||||
let flake = this._generate( now, this.mutable.increment );
|
|
||||||
this._unlock();
|
|
||||||
return flake;
|
|
||||||
}
|
|
||||||
|
|
||||||
deconstruct( snowflake ) {
|
|
||||||
// turn snowflake into a bigint
|
|
||||||
let flake = big( snowflake );
|
|
||||||
// shift right, and add epoch to obtain timestamp
|
|
||||||
let timestamp = flake.shiftRight( 22 ).add( this.options.epoch );
|
|
||||||
|
|
||||||
//obtain workerId
|
|
||||||
let wBitShift = this.options.incrementBits + this.options.processBits;
|
|
||||||
let workerId = flake.and( big( getBits( this.options.workerBits ) ).shiftLeft( wBitShift ) ).shiftRight( wBitShift );
|
|
||||||
|
|
||||||
// obtain processId
|
|
||||||
let processId = flake.and( big( getBits( this.options.processBits ) ).shiftLeft( this.options.incrementBits ) ).shiftRight( this.options.incrementBits );
|
|
||||||
|
|
||||||
// obtain increment
|
|
||||||
let increment = flake.and(getBits(this.options.incrementBits));
|
|
||||||
|
|
||||||
return { timestamp, workerId, processId, increment };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
68
index.d.ts
vendored
Normal file
68
index.d.ts
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Type definitions for Snowflakey 0.1.0
|
||||||
|
// Project: Snowflakey
|
||||||
|
// Definitions by: Wessel "wesselgame" T <discord@go2it.eu>
|
||||||
|
declare module 'snowflakey' {
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
export type Snowflake = number;
|
||||||
|
export function lookup(flake: number, epoch: number);
|
||||||
|
|
||||||
|
export class Master extends EventEmitter {
|
||||||
|
public workers: SnowflakeWorker[];
|
||||||
|
public refresh(): void;
|
||||||
|
public listWorkers(): SnowflakeWorker[];
|
||||||
|
public addWorkers(...workers: any[]): void;
|
||||||
|
public removeWorkers(...workers: string[] | number[]): { removed: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Worker extends EventEmitter {
|
||||||
|
public options: SnowflakeConfig;
|
||||||
|
private _mutable: SnowflakeMutable;
|
||||||
|
constructor(options: SnowflakeConfig);
|
||||||
|
private _lock(): void;
|
||||||
|
private _unlock(): void;
|
||||||
|
public generate(): Snowflake;
|
||||||
|
public deconstruct(flake: Snowflake): DeconstructedSnowflake;
|
||||||
|
private _generate(): Snowflake;
|
||||||
|
private _generateAsync(): Snowflake;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeconstructedSnowflake {
|
||||||
|
workerId: number,
|
||||||
|
timestamp: number,
|
||||||
|
processId: number,
|
||||||
|
increment: number
|
||||||
|
}
|
||||||
|
export interface SnowflakeConfig {
|
||||||
|
name?: string;
|
||||||
|
async?: boolean;
|
||||||
|
epoch: number;
|
||||||
|
workerId?: any,
|
||||||
|
processId?: number,
|
||||||
|
stringify?: boolean,
|
||||||
|
workerBits: number,
|
||||||
|
processBits: number,
|
||||||
|
incrementBits: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SnowflakeMutable {
|
||||||
|
locks: any;
|
||||||
|
locked: boolean;
|
||||||
|
increment: any;
|
||||||
|
lastTimestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface SnowflakeWorker {
|
||||||
|
options: SnowflakeConfig;
|
||||||
|
workerId: number;
|
||||||
|
_mutable: SnowflakeMutable;
|
||||||
|
processId: number;
|
||||||
|
_maxIncrement: number;
|
||||||
|
_lock(): void;
|
||||||
|
_unlock(): void;
|
||||||
|
generate(): Snowflake;
|
||||||
|
_generate(): Snowflake;
|
||||||
|
_generateAsync(): Snowflake;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
lib/Master.ts
Normal file
48
lib/Master.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { SnowflakeWorker } from './types';
|
||||||
|
|
||||||
|
export default class SnowflakeMaster extends EventEmitter {
|
||||||
|
public workers: any[]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.setMaxListeners(1000);
|
||||||
|
this.workers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
addWorkers(...workers: SnowflakeWorker[]): void {
|
||||||
|
for (const worker of workers) {
|
||||||
|
this.workers.push(worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
listWorkers(): SnowflakeWorker[] {
|
||||||
|
return this.workers;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeWorkers(...identities: string[] | number[]): { 'removed': number } {
|
||||||
|
let found = 0;
|
||||||
|
|
||||||
|
for (const identity of identities) {
|
||||||
|
for (let i in this.workers) {
|
||||||
|
const worker = this.workers[i];
|
||||||
|
if (worker.options.name === identity || worker.options.workerId === identity) {
|
||||||
|
found++;
|
||||||
|
this.workers.splice(parseInt(i), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { removed: found }
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh(): void {
|
||||||
|
for (let worker of this.workers) {
|
||||||
|
worker.on('newSnowflake', (...args) => this.emit('newSnowflake', ...args));
|
||||||
|
worker.on('deconstructedFlake', (...args) => this.emit('deconstructedFlake', ...args));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
156
lib/Worker.ts
Normal file
156
lib/Worker.ts
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import big from './bigInt';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { sleep, getBits } from './util'
|
||||||
|
import { Snowflake, SnowflakeConfig, SnowflakeMutable } from './types';
|
||||||
|
|
||||||
|
export default class SnowflakeWorker extends EventEmitter {
|
||||||
|
public options: SnowflakeConfig;
|
||||||
|
private _mutable: SnowflakeMutable;
|
||||||
|
private _maxIncrement: Number;
|
||||||
|
public workerId: number;
|
||||||
|
public processId: number;
|
||||||
|
|
||||||
|
constructor(options: SnowflakeConfig) {
|
||||||
|
super();
|
||||||
|
// The default options for the generator
|
||||||
|
this.setMaxListeners(100);
|
||||||
|
this.options = {
|
||||||
|
name: undefined,
|
||||||
|
async: false,
|
||||||
|
epoch: null,
|
||||||
|
workerId: 0,
|
||||||
|
processId: 0,
|
||||||
|
stringify: true,
|
||||||
|
workerBits: 5,
|
||||||
|
processBits: 5,
|
||||||
|
incrementBits: 12,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
// an object containing mutable (unfrozen) properties
|
||||||
|
this._mutable = {
|
||||||
|
locks: [],
|
||||||
|
locked: false,
|
||||||
|
increment: big.zero.subtract(1),
|
||||||
|
lastTimestamp: Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.options.incrementBits + this.options.processBits + this.options.workerBits !== 22) throw new Error('incrementBits, processBits, and workerBits must add up to 22.');
|
||||||
|
// ensure that ids conform to the number of bits
|
||||||
|
this.options.workerId = this.options.workerId % (2 ** this.options.workerBits);
|
||||||
|
this.options.processId = this.options.processId % (2 ** this.options.processBits);
|
||||||
|
|
||||||
|
// check if NaN
|
||||||
|
if (isNaN(this.options.workerId)) this.options.workerId = 0;
|
||||||
|
if (isNaN(this.options.processId)) this.options.processId = 0;
|
||||||
|
|
||||||
|
// store the maximum increment bound
|
||||||
|
this._maxIncrement = 2 ** this.options.incrementBits;
|
||||||
|
|
||||||
|
// calculate the shifted worker/process ids for later reference
|
||||||
|
this.workerId = big(this.options.workerId).shiftLeft(this.options.incrementBits + this.options.processBits);
|
||||||
|
this.processId = big(this.options.processId).shiftLeft(this.options.incrementBits);
|
||||||
|
|
||||||
|
// freeze immutable objects to prevent tampering
|
||||||
|
Object.freeze(this.options);
|
||||||
|
// Object.freeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get increment(): number {
|
||||||
|
return this._mutable.increment = this._mutable.increment.next().mod(this._maxIncrement);
|
||||||
|
}
|
||||||
|
|
||||||
|
generate(): Snowflake | Promise<Snowflake> {
|
||||||
|
if (this.options.async) return this._generateAsync();
|
||||||
|
else return this._generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
_generate(date: number = Date.now(), increment: number = this.increment): Snowflake {
|
||||||
|
// 0000000000000000000000000000000000000000000000000000000000000000
|
||||||
|
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0000000000000000000000
|
||||||
|
let flake: any = big(date).minus(this.options.epoch).shiftLeft(22)
|
||||||
|
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbb00000000000000000
|
||||||
|
.add(this.workerId)
|
||||||
|
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbccccc000000000000
|
||||||
|
.add(this.processId)
|
||||||
|
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbcccccdddddddddddd
|
||||||
|
.add(increment);
|
||||||
|
|
||||||
|
this.emit('newSnowflake', {
|
||||||
|
worker: this,
|
||||||
|
method: 'sync',
|
||||||
|
snowflake: flake.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.options.stringify) flake = flake.toString();
|
||||||
|
return flake;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lock() {
|
||||||
|
if (this._mutable.locked) return new Promise(res => this._mutable.locks.push(res));
|
||||||
|
else this._mutable.locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_unlock(): void {
|
||||||
|
if (this._mutable.locks.length > 0) this._mutable.locks.shift()();
|
||||||
|
else this._mutable.locked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _generateAsync(): Promise<Snowflake> {
|
||||||
|
let lock = this._lock();
|
||||||
|
if (lock) await lock;
|
||||||
|
let now = Date.now();
|
||||||
|
// check if increment should be reset
|
||||||
|
if (this._mutable.lastTimestamp !== now) {
|
||||||
|
// last timestamp didnt match, reset increment
|
||||||
|
this._mutable.increment = big.zero;
|
||||||
|
this._mutable.lastTimestamp = now;
|
||||||
|
} else {
|
||||||
|
// last timestamp matched, increase increment
|
||||||
|
this._mutable.increment = this._mutable.increment.next();
|
||||||
|
// check if increment exceeds max bounds
|
||||||
|
if (this._mutable.increment.greaterOrEquals(this._maxIncrement)) {
|
||||||
|
// sleep for 2ms - 1ms has a risk of timestamp not incrementing for some reason?
|
||||||
|
await sleep(2 / 1000);
|
||||||
|
// reset increment
|
||||||
|
this._mutable.increment = big.zero;
|
||||||
|
now = this._mutable.lastTimestamp = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a snowflake with the new increment
|
||||||
|
let flake: Snowflake = this._generate(now, this._mutable.increment);
|
||||||
|
this._unlock();
|
||||||
|
this.emit('newSnowflake', {
|
||||||
|
worker: this,
|
||||||
|
method: 'async',
|
||||||
|
snowflake: flake.toString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return flake;
|
||||||
|
}
|
||||||
|
|
||||||
|
deconstruct(snowflake: Snowflake): object {
|
||||||
|
// turn snowflake into a bigint
|
||||||
|
let flake = big(snowflake);
|
||||||
|
// shift right, and add epoch to obtain timestamp
|
||||||
|
let timestamp = flake.shiftRight(22).add(this.options.epoch);
|
||||||
|
|
||||||
|
//obtain workerId
|
||||||
|
let wBitShift = this.options.incrementBits + this.options.processBits;
|
||||||
|
let workerId = flake.and(big(getBits(this.options.workerBits)).shiftLeft(wBitShift)).shiftRight(wBitShift);
|
||||||
|
|
||||||
|
// obtain processId
|
||||||
|
let processId = flake.and(big(getBits(this.options.processBits)).shiftLeft(this.options.incrementBits)).shiftRight(this.options.incrementBits);
|
||||||
|
|
||||||
|
// obtain increment
|
||||||
|
let increment = flake.and(getBits(this.options.incrementBits));
|
||||||
|
|
||||||
|
this.emit('deconstructedFlake', {
|
||||||
|
worker: this,
|
||||||
|
method: 'sync',
|
||||||
|
timestamp, workerId, processId, increment
|
||||||
|
});
|
||||||
|
return { timestamp, workerId, processId, increment };
|
||||||
|
}
|
||||||
|
};
|
||||||
6
lib/snowflakey.ts
Normal file
6
lib/snowflakey.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const Master = require('./Master').default;
|
||||||
|
export const Worker = require('./Worker').default;
|
||||||
|
export const lookup = (flake: number, epoch: number): string => {
|
||||||
|
return new Date((flake / 4194304) + epoch).toLocaleString();
|
||||||
|
};
|
||||||
|
|
||||||
33
lib/types.ts
Normal file
33
lib/types.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
export interface SnowflakeConfig {
|
||||||
|
name?: string;
|
||||||
|
async?: boolean;
|
||||||
|
epoch?: number;
|
||||||
|
workerId?: any,
|
||||||
|
processId?: number,
|
||||||
|
stringify?: boolean,
|
||||||
|
workerBits: number,
|
||||||
|
processBits: number,
|
||||||
|
incrementBits: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SnowflakeMutable {
|
||||||
|
locks: any;
|
||||||
|
locked: boolean;
|
||||||
|
increment: any;
|
||||||
|
lastTimestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Snowflake = number;
|
||||||
|
|
||||||
|
export interface SnowflakeWorker {
|
||||||
|
options: SnowflakeConfig;
|
||||||
|
workerId: number;
|
||||||
|
_mutable: SnowflakeMutable;
|
||||||
|
processId: number;
|
||||||
|
_maxIncrement: number;
|
||||||
|
_lock(): void;
|
||||||
|
_unlock(): void;
|
||||||
|
generate(): Snowflake;
|
||||||
|
_generate(): Snowflake;
|
||||||
|
_generateAsync(): Snowflake;
|
||||||
|
}
|
||||||
18
lib/util.ts
Normal file
18
lib/util.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Halt the event loop for `duration` seconds
|
||||||
|
*
|
||||||
|
* @param {number} [duration=1] - The duration in seconds to wait for
|
||||||
|
*/
|
||||||
|
export const sleep = (duration: number = 1): void => {
|
||||||
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, (duration * 1000));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all bits from `bits`
|
||||||
|
*
|
||||||
|
* @param {number} bits - The bits to get bits from
|
||||||
|
* @returns {number} - The found bits
|
||||||
|
*/
|
||||||
|
export const getBits = (bits: number): number => {
|
||||||
|
return (2 ** bits) - 1;
|
||||||
|
};
|
||||||
23
package.json
23
package.json
@@ -1,14 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "snowflakey",
|
"name": "snowflakey",
|
||||||
"version": "0.0.2",
|
"version": "0.1.1",
|
||||||
"description": "❄️ Snowflake generation/lookup",
|
"description": "❄️ Fast and easy snowflake generation and lookup",
|
||||||
"author": "Wessel \"wesselgame\" T <discord@go2it.eu>",
|
"author": "Wessel \"wesselgame\" T <discord@go2it.eu>",
|
||||||
"main": "generator.js",
|
"main": "dist/lib/snowflakey.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
"generator.js",
|
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"fake_node_modules/"
|
"dist/lib",
|
||||||
|
"index.d.ts"
|
||||||
],
|
],
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://www.github.com/PassTheWessel/Snowflakey/issues"
|
"url": "https://www.github.com/PassTheWessel/Snowflakey/issues"
|
||||||
@@ -21,9 +22,17 @@
|
|||||||
"keywords": [
|
"keywords": [
|
||||||
"snowflake",
|
"snowflake",
|
||||||
"generator",
|
"generator",
|
||||||
"cli",
|
|
||||||
"accounts",
|
"accounts",
|
||||||
"lookup",
|
"lookup",
|
||||||
"lightweight"
|
"lightweight"
|
||||||
]
|
],
|
||||||
|
"scripts": {
|
||||||
|
"test": "cd ./dist/tests && node main.test.js",
|
||||||
|
"compile": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^12.6.8",
|
||||||
|
"@typescript-eslint/parser": "^1.13.0"
|
||||||
|
},
|
||||||
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
18
test.js
18
test.js
@@ -1,18 +0,0 @@
|
|||||||
// require & generate the instance
|
|
||||||
const Snowflake = require( './generator' );
|
|
||||||
const snowflake = new Snowflake.generator({
|
|
||||||
processBits: 0,
|
|
||||||
workerBits: 8,
|
|
||||||
incrementBits: 14,
|
|
||||||
workerId: process.env.CLUSTER_ID || 31
|
|
||||||
});
|
|
||||||
|
|
||||||
// exports for global use
|
|
||||||
exports.makeSnowflake = ( date ) => { return snowflake._generate( date ); };
|
|
||||||
exports.unmakeSnowflake = ( flake ) => { let decon = snowflake.deconstruct( flake ); return decon.timestamp.valueOf(); };
|
|
||||||
|
|
||||||
// example
|
|
||||||
const flake = this.makeSnowflake( Date.now() );
|
|
||||||
console.log( flake );
|
|
||||||
console.log( `Creation date: ${Snowflake.lookup( flake, 1420070400000 )}` );
|
|
||||||
console.log( this.unmakeSnowflake( flake ) );
|
|
||||||
40
tests/main.test.ts
Normal file
40
tests/main.test.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// require & generate the instance
|
||||||
|
import * as Snowflake from '../lib/snowflakey';
|
||||||
|
|
||||||
|
const master = new Snowflake.Master();
|
||||||
|
const worker = new Snowflake.Worker({
|
||||||
|
name: 'starling',
|
||||||
|
epoch: 1420070400000,
|
||||||
|
workerId: process.env.CLUSTER_ID || 31,
|
||||||
|
processId: process.pid || undefined,
|
||||||
|
workerBits: 8,
|
||||||
|
processBits: 0,
|
||||||
|
incrementBits: 14
|
||||||
|
});
|
||||||
|
|
||||||
|
master.addWorkers(worker);
|
||||||
|
|
||||||
|
// Using the worker directly
|
||||||
|
const flake = worker.generate();
|
||||||
|
const epoch = 1420070400000;
|
||||||
|
|
||||||
|
console.log('----------[ Worker ]----------')
|
||||||
|
console.log(`Created snowflake: ${flake}`);
|
||||||
|
console.log(`Creation date : ${Snowflake.lookup(flake, worker.options.epoch)}`);
|
||||||
|
console.log(`Deconstructed : ${worker.deconstruct(flake).timestamp.valueOf()}`);
|
||||||
|
// Using the master to get events
|
||||||
|
console.log('----------[ Master ]----------')
|
||||||
|
master.on('newSnowflake', (data) => {
|
||||||
|
console.log(`created snowflake: ${data.snowflake} by Worker ${data.worker.options.name || data.worker.options.workerId}`)
|
||||||
|
console.log(`Creation date : ${Snowflake.lookup(flake, data.worker.options.epoch)}`);
|
||||||
|
data.worker.deconstruct(data.snowflake);
|
||||||
|
});
|
||||||
|
|
||||||
|
master.on('deconstructedFlake', (data) => {
|
||||||
|
console.log(`Deconstructed : ${data.timestamp.valueOf()} by Worker ${data.worker.options.name || data.worker.options.workerId}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.generate();
|
||||||
|
|
||||||
|
master.removeWorkers(worker.options.name);
|
||||||
|
console.log(master.listWorkers())
|
||||||
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [ "es2015", "es2016", "es2017", "esnext" ],
|
||||||
|
"watch": true,
|
||||||
|
"types": [ "node" ],
|
||||||
|
"outDir": "./dist",
|
||||||
|
"target": "esnext",
|
||||||
|
"module": "commonjs",
|
||||||
|
"strict": false,
|
||||||
|
"allowJs": true,
|
||||||
|
"rootDirs": [ "./lib", "./tests" ],
|
||||||
|
"declaration": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"removeComments": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [ "lib/**/*", "tests/**/*" ],
|
||||||
|
"exclude": [ "node_modules" ]
|
||||||
|
}
|
||||||
80
yarn.lock
Normal file
80
yarn.lock
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@types/eslint-visitor-keys@^1.0.0":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||||
|
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
|
||||||
|
|
||||||
|
"@types/json-schema@^7.0.3":
|
||||||
|
version "7.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
|
||||||
|
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
|
||||||
|
|
||||||
|
"@types/node@^12.6.8":
|
||||||
|
version "12.6.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.6.8.tgz#e469b4bf9d1c9832aee4907ba8a051494357c12c"
|
||||||
|
integrity sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==
|
||||||
|
|
||||||
|
"@typescript-eslint/experimental-utils@1.13.0":
|
||||||
|
version "1.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e"
|
||||||
|
integrity sha512-zmpS6SyqG4ZF64ffaJ6uah6tWWWgZ8m+c54XXgwFtUv0jNz8aJAVx8chMCvnk7yl6xwn8d+d96+tWp7fXzTuDg==
|
||||||
|
dependencies:
|
||||||
|
"@types/json-schema" "^7.0.3"
|
||||||
|
"@typescript-eslint/typescript-estree" "1.13.0"
|
||||||
|
eslint-scope "^4.0.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/parser@^1.13.0":
|
||||||
|
version "1.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355"
|
||||||
|
integrity sha512-ITMBs52PCPgLb2nGPoeT4iU3HdQZHcPaZVw+7CsFagRJHUhyeTgorEwHXhFf3e7Evzi8oujKNpHc8TONth8AdQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/eslint-visitor-keys" "^1.0.0"
|
||||||
|
"@typescript-eslint/experimental-utils" "1.13.0"
|
||||||
|
"@typescript-eslint/typescript-estree" "1.13.0"
|
||||||
|
eslint-visitor-keys "^1.0.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree@1.13.0":
|
||||||
|
version "1.13.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e"
|
||||||
|
integrity sha512-b5rCmd2e6DCC6tCTN9GSUAuxdYwCM/k/2wdjHGrIRGPSJotWMCe/dGpi66u42bhuh8q3QBzqM4TMA1GUUCJvdw==
|
||||||
|
dependencies:
|
||||||
|
lodash.unescape "4.0.1"
|
||||||
|
semver "5.5.0"
|
||||||
|
|
||||||
|
eslint-scope@^4.0.0:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
|
||||||
|
integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==
|
||||||
|
dependencies:
|
||||||
|
esrecurse "^4.1.0"
|
||||||
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
|
eslint-visitor-keys@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||||
|
integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==
|
||||||
|
|
||||||
|
esrecurse@^4.1.0:
|
||||||
|
version "4.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
|
||||||
|
integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==
|
||||||
|
dependencies:
|
||||||
|
estraverse "^4.1.0"
|
||||||
|
|
||||||
|
estraverse@^4.1.0, estraverse@^4.1.1:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
|
||||||
|
integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
|
||||||
|
|
||||||
|
lodash.unescape@4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
|
||||||
|
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
|
||||||
|
|
||||||
|
semver@5.5.0:
|
||||||
|
version "5.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||||
|
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
|
||||||
Reference in New Issue
Block a user