diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5dd87bd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+package-lock.json
+yarn.lock
+.nyc_output/
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..43c97e7
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+package-lock=false
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0c1dd10
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,14 @@
+language: node_js
+
+node_js:
+ - "8"
+ - "10"
+ - "12"
+ - "13"
+
+script:
+ - npm run test-cov
+
+after_script:
+ - npm install coveralls
+ - nyc report --reporter=text-lcov | coveralls
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..17304de
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 dailyrandomphoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0558234
--- /dev/null
+++ b/README.md
@@ -0,0 +1,105 @@
+# id-generators
+
+[![NPM Version][npm-version-image]][npm-url]
+[![LICENSE][license-image]][license-url]
+[![Build Status][travis-image]][travis-url]
+[![Coverage Status][coveralls-image]][coveralls-url]
+[![dependencies Status][dependencies-image]][dependencies-url]
+[![devDependencies Status][devDependencies-image]][devDependencies-url]
+
+**id-generators** is small JavaScript library to generate ID with awesome Unique ID libraries.
+
+## Installation
+
+```sh
+npm install id-generators
+```
+
+## Usages
+
+```js
+const generators = require('id-generators');
+const generator = generators.get('nanoid');
+const generate = generator();
+
+console.log('ID: ' + generate());
+// ID: gCa0wL_8ElTje5cwOci1d
+console.log('ID: ' + generate());
+// ID: 5C_YAjgQl4iM3zK-TValY
+console.log('ID: ' + generate());
+// ID: gQOOwCoQyfCuGK7fgINLd
+```
+
+If you want to customize ID, you can pass an option as an argument to the `generator` function.
+```js
+const generators = require('id-generators');
+const generator = generators.get('nanoid-simple');
+const generate = generator({size: 25});
+
+console.log('ID: ' + generate());
+// ID: oa9wj0kfm50gv2qse8l5xup0u
+console.log('ID: ' + generate());
+// ID: xn5ff5odiylg4jehhhp9vtlxv
+console.log('ID: ' + generate());
+// ID: dff7ay5x38k1pkc2pxjv7elky
+```
+
+generator type | ID length | character set |options/default | description
+--- | --- | --- | --- | ---
+cuid (default) | 25 | `a-z0-9`, start with `c` | | use [cuid()](https://github.com/ericelliott/cuid) generated string.
e.g. `ck2bi7fxf00013ryng5jr1rer`
+cuid-slug | 7-10 | `a-z0-9` | | use [cuid.slug()](https://github.com/ericelliott/cuid) generated string.
e.g. `xh23npi`
+nanoid | 21 | `A-Za-z0-9_-` | size/21 | use [nanoid()](https://github.com/ai/nanoid) generated string.
e.g. `EwUTt2eoka-oEV5kf-o0O`
+nanoid-simple | 24 | `a-z0-9` | size/24 | use [nanoid/generate](https://github.com/ai/nanoid) generated string.
e.g. `pfldm3gg8h9psydphotqe71d`
+nanoid-lowercase | 26 | `a-z` | size/26 | use [nanoid/generate](https://github.com/ai/nanoid) generated string.
e.g. `jsjxoibprplrdoitjmppotjrnm`
+
+
+## Define Custom Generators
+This sample shows how to register a generator function.
+The generator function should return a function that returns a ID.
+
+```js
+const { register } = require('id-generators');
+
+register('my_custom_id', function(option) {
+ option = option || {};
+ let size = option.size || 8;
+ let prefix = option.prefix || 'items-';
+ return function(title) {
+ return prefix + title.toLowerCase().replace(/[^\w]/g, '').substring(0, size);
+ };
+});
+```
+```js
+const generators = require('id-generators');
+const generator = generators.get('my_custom_id');
+const generate = generator({size: 6});
+
+console.log('ID: ' + generate('Hello World!'));
+// ID: items-hellow
+console.log('ID: ' + generate('Foo Bar!'));
+// ID: items-foobar
+```
+
+## Related
+- [Awesome Unique ID](https://github.com/grantcarthew/awesome-unique-id) - A curated list of awesome Unique ID libraries and resources.
+- [cuid](https://github.com/ericelliott/cuid) - Collision-resistant ids optimized for horizontal scaling and binary search lookup performance.
+- [nanoid](https://github.com/ai/nanoid) - A tiny, secure, URL-friendly, unique string ID generator for JavaScript.
+
+
+## License
+Copyright (c) 2019 dailyrandomphoto. Licensed under the [MIT license][license-url].
+
+[npm-url]: https://www.npmjs.com/package/id-generators
+[travis-url]: https://travis-ci.org/dailyrandomphoto/id-generators
+[coveralls-url]: https://coveralls.io/github/dailyrandomphoto/id-generators?branch=master
+[license-url]: LICENSE
+[dependencies-url]: https://david-dm.org/dailyrandomphoto/id-generators
+[devDependencies-url]: https://david-dm.org/dailyrandomphoto/id-generators?type=dev
+
+[npm-downloads-image]: https://img.shields.io/npm/dm/id-generators.svg
+[npm-version-image]: https://img.shields.io/npm/v/id-generators.svg
+[license-image]: https://img.shields.io/npm/l/id-generators.svg
+[travis-image]: https://img.shields.io/travis/dailyrandomphoto/id-generators/master
+[coveralls-image]: https://coveralls.io/repos/github/dailyrandomphoto/id-generators/badge.svg?branch=master
+[dependencies-image]: https://david-dm.org/dailyrandomphoto/id-generators/status.svg
+[devDependencies-image]: https://david-dm.org/dailyrandomphoto/id-generators/dev-status.svg
diff --git a/lib/cuid-slug.js b/lib/cuid-slug.js
new file mode 100644
index 0000000..3f9dad6
--- /dev/null
+++ b/lib/cuid-slug.js
@@ -0,0 +1,7 @@
+'use strict';
+
+const {slug} = require('cuid');
+
+module.exports = function () {
+ return () => slug();
+};
diff --git a/lib/cuid.js b/lib/cuid.js
new file mode 100644
index 0000000..6e35121
--- /dev/null
+++ b/lib/cuid.js
@@ -0,0 +1,7 @@
+'use strict';
+
+const cuid = require('cuid');
+
+module.exports = function () {
+ return cuid;
+};
diff --git a/lib/id-generators.js b/lib/id-generators.js
new file mode 100644
index 0000000..9ef5bb7
--- /dev/null
+++ b/lib/id-generators.js
@@ -0,0 +1,29 @@
+'use strict';
+
+const store = {};
+
+function register(type, fn) {
+ if (typeof fn !== 'function') {
+ throw new TypeError('fn must be a function');
+ }
+
+ if (typeof store[type] === 'function') {
+ throw new TypeError('already registered as type: ' + type);
+ }
+
+ store[type] = fn;
+}
+
+function get(type) {
+ type = type || 'default';
+ return store[type] || throwIt(new TypeError('Can\'t find generator with type: ' + type));
+}
+
+function throwIt(err) {
+ throw err;
+}
+
+module.exports = {
+ register,
+ get
+};
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..31e6380
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,16 @@
+'use strict';
+
+const {register, get} = require('./id-generators');
+const cuid = require('./cuid');
+
+register('default', cuid);
+register('cuid', cuid);
+register('cuid-slug', require('./cuid-slug'));
+register('nanoid', require('./nanoid'));
+register('nanoid-simple', require('./nanoid-simple'));
+register('nanoid-lowercase', require('./nanoid-lowercase'));
+
+module.exports = {
+ register,
+ get
+};
diff --git a/lib/nanoid-lowercase.js b/lib/nanoid-lowercase.js
new file mode 100644
index 0000000..a1e799f
--- /dev/null
+++ b/lib/nanoid-lowercase.js
@@ -0,0 +1,13 @@
+'use strict';
+
+/**
+ * https://github.com/ai/nanoid
+ */
+const generate = require('nanoid/generate');
+const alphabet = 'abcdefghijklmnopqrstuvwxyz';
+
+module.exports = function (option) {
+ option = option || {};
+ const size = option.size || 26;
+ return () => generate(alphabet, size);
+};
diff --git a/lib/nanoid-simple.js b/lib/nanoid-simple.js
new file mode 100644
index 0000000..e379bf4
--- /dev/null
+++ b/lib/nanoid-simple.js
@@ -0,0 +1,13 @@
+'use strict';
+
+/**
+ * https://github.com/ai/nanoid
+ */
+const generate = require('nanoid/generate');
+const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
+
+module.exports = function (option) {
+ option = option || {};
+ const size = option.size || 24;
+ return () => generate(alphabet, size);
+};
diff --git a/lib/nanoid.js b/lib/nanoid.js
new file mode 100644
index 0000000..1281142
--- /dev/null
+++ b/lib/nanoid.js
@@ -0,0 +1,12 @@
+'use strict';
+
+/**
+ * https://github.com/ai/nanoid
+ */
+const nanoid = require('nanoid');
+
+module.exports = function (option) {
+ option = option || {};
+ const size = option.size || 21;
+ return () => nanoid(size);
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fd06d35
--- /dev/null
+++ b/package.json
@@ -0,0 +1,58 @@
+{
+ "name": "id-generators",
+ "version": "1.0.0",
+ "description": "id-generators is small JavaScript library to generate ID with awesome Unique ID libraries.",
+ "main": "lib/index.js",
+ "scripts": {
+ "eslint": "xo --fix",
+ "test": "xo && mocha test/index.js",
+ "test-cov": "nyc npm run test"
+ },
+ "dependencies": {
+ "cuid": "^2.1.6",
+ "nanoid": "^2.1.6"
+ },
+ "devDependencies": {
+ "chai": "^4.2.0",
+ "mocha": "^6.2.1",
+ "nyc": "^14.1.1",
+ "xo": "^0.25.3"
+ },
+ "keywords": [
+ "generate",
+ "generator",
+ "id",
+ "uuid",
+ "unique-id",
+ "cuid",
+ "nanoid",
+ "url"
+ ],
+ "files": [
+ "lib/",
+ "LICENSE",
+ "README.md"
+ ],
+ "engines": {
+ "node": ">=8.6.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/dailyrandomphoto/id-generators.git"
+ },
+ "author": "dailyrandomphoto (https://www.dailyrandomphoto.com/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/dailyrandomphoto/id-generators/issues"
+ },
+ "homepage": "https://github.com/dailyrandomphoto/id-generators#readme",
+ "xo": {
+ "space": 2,
+ "overrides": [
+ {
+ "files": "test/*.js",
+ "envs": ["node", "mocha"]
+ }
+ ]
+ }
+}
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..dc5d6de
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,152 @@
+'use strict';
+
+const {expect} = require('chai');
+const generators = require('../lib');
+
+describe('id-generators', () => {
+ it('should throw an error when register with an invalid argument', () => {
+ expect(() => generators.register('invalid', 'not a function')).to.throw(TypeError);
+ });
+
+ it('should throw an error when register with an already registered key', () => {
+ expect(() => generators.register('cuid', () => {})).to.throw(TypeError);
+ });
+
+ it('should use the custom function', () => {
+ generators.register('my_custom_func', () => {
+ return () => new Date().getTime();
+ });
+
+ const generator = generators.get('my_custom_func');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('number');
+ });
+
+ it('should use option in the custom function', () => {
+ generators.register('my_custom_path', option => {
+ option = option || {};
+ const size = option.size || 8;
+ const prefix = option.prefix || 'items-';
+ return function (title) {
+ return prefix + title.toLowerCase().replace(/[^\w]/g, '').slice(0, size);
+ };
+ });
+
+ const generator = generators.get('my_custom_path');
+ generator.should.be.a('function');
+ const gen = generator({size: 6});
+ gen.should.be.a('function');
+ const id = gen('Hello World!');
+ console.log(' ID: ' + id);
+ id.should.eql('items-hellow');
+ });
+
+ it('should throw an error when get a unregistered generator', () => {
+ expect(() => generators.get('unregistered')).to.throw(TypeError);
+ });
+
+ it('should return a default function', () => {
+ const generator = generators.get();
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string');
+ });
+
+ describe('cuid', () => {
+ it('should return a string with length is 25', () => {
+ const generator = generators.get('cuid');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(25);
+ id.slice(0, 1).should.eql('c');
+ });
+ });
+
+ describe('cuid-slug', () => {
+ it('should return a string with length is 7 to 10', () => {
+ const generator = generators.get('cuid-slug');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf.within(7, 10);
+ });
+ });
+
+ describe('nanoid', () => {
+ it('should return a string with length is 21', () => {
+ const generator = generators.get('nanoid');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(21);
+ });
+
+ it('should return a string with a custom length', () => {
+ const generator = generators.get('nanoid');
+ generator.should.be.a('function');
+ const gen = generator({size: 18});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(18);
+ });
+ });
+
+ describe('nanoid-simple', () => {
+ it('should return a string with length is 24', () => {
+ const generator = generators.get('nanoid-simple');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(24);
+ });
+
+ it('should return a string with a custom length', () => {
+ const generator = generators.get('nanoid-simple');
+ generator.should.be.a('function');
+ const gen = generator({size: 18});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(18);
+ });
+ });
+
+ describe('nanoid-lowercase', () => {
+ it('should return a string with length is 26', () => {
+ const generator = generators.get('nanoid-lowercase');
+ generator.should.be.a('function');
+ const gen = generator({});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(26);
+ });
+
+ it('should return a string with a custom length', () => {
+ const generator = generators.get('nanoid-lowercase');
+ generator.should.be.a('function');
+ const gen = generator({size: 18});
+ gen.should.be.a('function');
+ const id = gen();
+ console.log(' ID: ' + id);
+ id.should.be.a('string').have.lengthOf(18);
+ });
+ });
+});
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 0000000..efa7cb0
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1,6 @@
+--colors
+--reporter spec
+--ui bdd
+--full-trace
+--exit
+--require chai/register-should