diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..63187fe --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 80 +trim_trailing_whitespace = true + +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..5ac541a --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Example Contributing Guidelines + +This is an example of GitHub's contributing guidelines file. Check out GitHub's [CONTRIBUTING.md help center article](https://help.github.com/articles/setting-guidelines-for-repository-contributors/) for more information. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3b7c28f --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ +* **I'm submitting a ...** +[ ] bug report +[ ] feature request +[ ] question about the decisions made in the repository +[ ] question about how to use this project + +* **Summary** + + + +* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..caae6b1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) + + + +* **What is the current behavior?** (You can also link to an open issue here) + + + +* **What is the new behavior (if this is a feature change)?** + + + +* **Other information**: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93e10ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules +build +test +src/**.js +.idea/* + +coverage +.nyc_output +*.log + +package-lock.json \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..845f94c --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +src +test +tsconfig.json +tsconfig.module.json +tslint.json +.travis.yml +.github +.prettierignore +.vscode +build/docs +**/*.spec.* +coverage +.nyc_output +*.log diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0e80a3c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# package.json is formatted by package managers, so we ignore it here +package.json \ No newline at end of file diff --git a/.vscode/debug-ts.js b/.vscode/debug-ts.js new file mode 100644 index 0000000..37f6354 --- /dev/null +++ b/.vscode/debug-ts.js @@ -0,0 +1,52 @@ +'use strict'; +const meow = require('meow'); +const path = require('path'); + +const tsFile = getTSFile(); +const jsFile = TS2JS(tsFile); + +replaceCLIArg(tsFile, jsFile); + +// Ava debugger +require('ava/profile'); + +/** + * get ts file path from CLI args + * + * @return string path + */ +function getTSFile() { + const cli = meow(); + return cli.input[0]; +} + +/** + * get associated compiled js file path + * + * @param tsFile path + * @return string path + */ +function TS2JS(tsFile) { + const srcFolder = path.join(__dirname, '..', 'src'); + const distFolder = path.join(__dirname, '..', 'build', 'main'); + + const tsPathObj = path.parse(tsFile); + + return path.format({ + dir: tsPathObj.dir.replace(srcFolder, distFolder), + ext: '.js', + name: tsPathObj.name, + root: tsPathObj.root + }); +} + +/** + * replace a value in CLI args + * + * @param search value to search + * @param replace value to replace + * @return void + */ +function replaceCLIArg(search, replace) { + process.argv[process.argv.indexOf(search)] = replace; +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..e695824 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,39 @@ +{ + "version": "0.2.0", + "configurations": [{ + "type": "node", + "request": "launch", + "name": "Debug Project", + // we test in `build` to make cleanup fast and easy + "cwd": "${workspaceFolder}/build", + // Replace this with your project root. If there are multiple, you can + // automatically run the currently visible file with: "program": ${file}" + "program": "${workspaceFolder}/src/cli/cli.ts", + // "args": ["--no-install"], + "outFiles": ["${workspaceFolder}/build/main/**/*.js"], + "skipFiles": [ + "/**/*.js", + "${workspaceFolder}/node_modules/**/*.js" + ], + "preLaunchTask": "npm: build", + "stopOnEntry": true, + "smartStep": true, + "runtimeArgs": ["--nolazy"], + "env": { + "TYPESCRIPT_STARTER_REPO_URL": "${workspaceFolder}" + }, + "console": "externalTerminal" + }, + { + "type": "node", + "request": "launch", + "name": "Debug Spec", + "program": "${workspaceRoot}/.vscode/debug-ts.js", + "args": ["${file}"], + "skipFiles": ["/**/*.js"], + // Consider using `npm run watch` or `yarn watch` for faster debugging + // "preLaunchTask": "npm: build", + // "smartStep": true, + "runtimeArgs": ["--nolazy"] + }] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..41d5c67 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" + // "typescript.implementationsCodeLens.enabled": true + // "typescript.referencesCodeLens.enabled": true +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f7eed55 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Christoph Witzany + +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..265e6f4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# musterroll + +A lightweight LDAP server for user management diff --git a/package.json b/package.json new file mode 100644 index 0000000..3cbf03d --- /dev/null +++ b/package.json @@ -0,0 +1,96 @@ +{ + "name": "musterroll", + "version": "1.0.0", + "description": "A lightweight LDAP server for user management", + "main": "build/main/index.js", + "typings": "build/main/index.d.ts", + "module": "build/module/index.js", + "repository": "https://github.com/DoubleMalt/musterroll", + "license": "MIT", + "keywords": [], + "scripts": { + "describe": "npm-scripts-info", + "build": "run-s clean && run-p build:*", + "build:main": "tsc -p tsconfig.json", + "build:module": "tsc -p tsconfig.module.json", + "fix": "run-s fix:*", + "fix:prettier": "prettier \"src/**/*.ts\" --write", + "fix:tslint": "tslint --fix --project .", + "test": "run-s build test:*", + "test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", + "test:unit": "nyc --silent ava", + "watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"", + "cov": "run-s build test:unit cov:html && opn coverage/index.html", + "cov:html": "nyc report --reporter=html", + "cov:send": "nyc report --reporter=lcov > coverage.lcov && codecov", + "cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100", + "doc": "run-s doc:html && opn build/docs/index.html", + "doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs", + "doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json", + "doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs", + "version": "standard-version", + "reset": "git clean -dfx && git reset --hard && npm i", + "clean": "trash build test", + "all": "run-s reset test cov:check doc:html", + "prepare-release": "run-s all version doc:publish", + "preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('musterroll must be installed with Yarn: https://yarnpkg.com/')\"" + }, + "scripts-info": { + "info": "Display information about the package scripts", + "build": "Clean and rebuild the project", + "fix": "Try to automatically fix any linting problems", + "test": "Lint and unit test the project", + "watch": "Watch and rebuild the project on save, then rerun relevant tests", + "cov": "Rebuild, run tests, then create and open the coverage report", + "doc": "Generate HTML API documentation and open it in a browser", + "doc:json": "Generate API documentation in typedoc JSON format", + "version": "Bump package.json version, update CHANGELOG.md, tag release", + "reset": "Delete all untracked files and reset the repo to the last commit", + "prepare-release": "One-step: clean, build, test, publish docs, and prep a release" + }, + "engines": { + "node": ">=8.9" + }, + "dependencies": { + "sha.js": "^2.4.11" + }, + "devDependencies": { + "ava": "1.0.0-beta.7", + "codecov": "^3.1.0", + "cz-conventional-changelog": "^2.1.0", + "gh-pages": "^2.0.1", + "npm-run-all": "^4.1.5", + "nyc": "^13.1.0", + "opn-cli": "^4.0.0", + "prettier": "^1.15.2", + "standard-version": "^4.4.0", + "trash-cli": "^1.4.0", + "tslint": "^5.11.0", + "tslint-config-prettier": "^1.17.0", + "tslint-immutable": "^5.0.0", + "typedoc": "^0.13.0", + "typescript": "^3.1.6" + }, + "ava": { + "failFast": true, + "files": [ + "build/main/**/*.spec.js" + ], + "sources": [ + "build/main/**/*.js" + ] + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "prettier": { + "singleQuote": true + }, + "nyc": { + "exclude": [ + "**/*.spec.js" + ] + } +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..91e164f --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './lib/async'; +export * from './lib/hash'; +export * from './lib/number'; diff --git a/src/lib/async.spec.ts b/src/lib/async.spec.ts new file mode 100644 index 0000000..2cb9695 --- /dev/null +++ b/src/lib/async.spec.ts @@ -0,0 +1,7 @@ +// tslint:disable:no-expression-statement +import test from 'ava'; +import { asyncABC } from './async'; + +test('getABC', async t => { + t.deepEqual(await asyncABC(), ['a', 'b', 'c']); +}); diff --git a/src/lib/async.ts b/src/lib/async.ts new file mode 100644 index 0000000..d1298d4 --- /dev/null +++ b/src/lib/async.ts @@ -0,0 +1,32 @@ +/** + * A sample async function (to demo Typescript's es7 async/await downleveling). + * + * ### Example (es imports) + * ```js + * import { asyncABC } from 'typescript-starter' + * console.log(await asyncABC()) + * // => ['a','b','c'] + * ``` + * + * ### Example (commonjs) + * ```js + * var double = require('typescript-starter').asyncABC; + * asyncABC().then(console.log); + * // => ['a','b','c'] + * ``` + * + * @returns a Promise which should contain `['a','b','c']` + */ +export async function asyncABC(): Promise> { + function somethingSlow(index: 0 | 1 | 2): Promise { + const storage = 'abc'.charAt(index); + return new Promise(resolve => + // later... + resolve(storage) + ); + } + const a = await somethingSlow(0); + const b = await somethingSlow(1); + const c = await somethingSlow(2); + return [a, b, c]; +} diff --git a/src/lib/hash.spec.ts b/src/lib/hash.spec.ts new file mode 100644 index 0000000..1bd1a05 --- /dev/null +++ b/src/lib/hash.spec.ts @@ -0,0 +1,18 @@ +// tslint:disable:no-expression-statement no-object-mutation +import test, { Macro } from 'ava'; +import { sha256, sha256Native } from './hash'; + +const hash: Macro = (t, input: string, expected: string) => { + t.is(sha256(input), expected); + t.is(sha256Native(input), expected); +}; + +hash.title = (providedTitle: string, input: string, expected: string) => + `${providedTitle}: ${input} => ${expected}`; + +test( + 'sha256', + hash, + 'test', + '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' +); diff --git a/src/lib/hash.ts b/src/lib/hash.ts new file mode 100644 index 0000000..4cc5cde --- /dev/null +++ b/src/lib/hash.ts @@ -0,0 +1,38 @@ +import { createHash } from 'crypto'; +import shaJs from 'sha.js'; + +/** + * Calculate the sha256 digest of a string. + * + * ### Example (es imports) + * ```js + * import { sha256 } from 'typescript-starter' + * sha256('test') + * // => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' + * ``` + * + * @returns sha256 message digest + */ +export function sha256(message: string): string { + return shaJs('sha256') + .update(message) + .digest('hex'); +} + +/** + * A faster implementation of [[sha256]] which requires the native Node.js module. Browser consumers should use [[sha256]], instead. + * + * ### Example (es imports) + * ```js + * import { sha256Native as sha256 } from 'typescript-starter' + * sha256('test') + * // => '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' + * ``` + * + * @returns sha256 message digest + */ +export function sha256Native(message: string): string { + return createHash('sha256') + .update(message) + .digest('hex'); +} diff --git a/src/lib/number.spec.ts b/src/lib/number.spec.ts new file mode 100644 index 0000000..28c1fd9 --- /dev/null +++ b/src/lib/number.spec.ts @@ -0,0 +1,11 @@ +// tslint:disable:no-expression-statement +import test from 'ava'; +import { double, power } from './number'; + +test('double', t => { + t.is(double(2), 4); +}); + +test('power', t => { + t.is(power(2, 4), 16); +}); diff --git a/src/lib/number.ts b/src/lib/number.ts new file mode 100644 index 0000000..c4b4525 --- /dev/null +++ b/src/lib/number.ts @@ -0,0 +1,46 @@ +/** + * Multiplies a value by 2. (Also a full example of Typedoc's functionality.) + * + * ### Example (es module) + * ```js + * import { double } from 'typescript-starter' + * console.log(double(4)) + * // => 8 + * ``` + * + * ### Example (commonjs) + * ```js + * var double = require('typescript-starter').double; + * console.log(double(4)) + * // => 8 + * ``` + * + * @param value Comment describing the `value` parameter. + * @returns Comment describing the return type. + * @anotherNote Some other value. + */ +export function double(value: number): number { + return value * 2; +} + +/** + * Raise the value of the first parameter to the power of the second using the es7 `**` operator. + * + * ### Example (es module) + * ```js + * import { power } from 'typescript-starter' + * console.log(power(2,3)) + * // => 8 + * ``` + * + * ### Example (commonjs) + * ```js + * var power = require('typescript-starter').power; + * console.log(power(2,3)) + * // => 8 + * ``` + */ +export function power(base: number, exponent: number): number { + // This is a proposed es7 operator, which should be transpiled by Typescript + return base ** exponent; +} diff --git a/src/types/example.d.ts b/src/types/example.d.ts new file mode 100644 index 0000000..a4b739a --- /dev/null +++ b/src/types/example.d.ts @@ -0,0 +1,34 @@ +/** + * If you import a dependency which does not include its own type definitions, + * TypeScript will try to find a definition for it by following the `typeRoots` + * compiler option in tsconfig.json. For this project, we've configured it to + * fall back to this folder if nothing is found in node_modules/@types. + * + * Often, you can install the DefinitelyTyped + * (https://github.com/DefinitelyTyped/DefinitelyTyped) type definition for the + * dependency in question. However, if no one has yet contributed definitions + * for the package, you may want to declare your own. (If you're using the + * `noImplicitAny` compiler options, you'll be required to declare it.) + * + * This is an example type definition for the `sha.js` package, used in hash.ts. + * + * (This definition was primarily extracted from: + * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/v8/index.d.ts + */ +declare module 'sha.js' { + export default function shaJs(algorithm: string): Hash; + + type Utf8AsciiLatin1Encoding = 'utf8' | 'ascii' | 'latin1'; + type HexBase64Latin1Encoding = 'latin1' | 'hex' | 'base64'; + + export interface Hash extends NodeJS.ReadWriteStream { + // tslint:disable:no-method-signature + update( + data: string | Buffer | DataView, + inputEncoding?: Utf8AsciiLatin1Encoding + ): Hash; + digest(): Buffer; + digest(encoding: HexBase64Latin1Encoding): string; + // tslint:enable:no-method-signature + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..53d6b3b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,45 @@ +{ + "compilerOptions": { + "target": "es2017", + "outDir": "build/main", + "rootDir": "src", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "inlineSourceMap": true, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + + "strict": true /* Enable all strict type-checking options. */, + + /* Strict Type-Checking Options */ + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true /* Enable strict checking of function types. */, + // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + + /* Debugging Options */ + "traceResolution": false /* Report module resolution log messages. */, + "listEmittedFiles": false /* Print names of generated files part of the compilation. */, + "listFiles": false /* Print names of files part of the compilation. */, + "pretty": true /* Stylize errors and messages using color and context. */, + + /* Experimental Options */ + // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + "lib": ["es2017"], + "types": ["node"], + "typeRoots": ["node_modules/@types", "src/types"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules/**"], + "compileOnSave": false +} diff --git a/tsconfig.module.json b/tsconfig.module.json new file mode 100644 index 0000000..dfb74fa --- /dev/null +++ b/tsconfig.module.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "target": "esnext", + "outDir": "build/module", + "module": "esnext" + }, + "exclude": [ + "node_modules/**" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..b954a5d --- /dev/null +++ b/tslint.json @@ -0,0 +1,34 @@ +{ + "extends": ["tslint:latest", "tslint-config-prettier", "tslint-immutable"], + "rules": { + "interface-name": [true, "never-prefix"], + // TODO: allow devDependencies only in **/*.spec.ts files: + // waiting on https://github.com/palantir/tslint/pull/3708 + "no-implicit-dependencies": [true, "dev"], + + /* tslint-immutable rules */ + // Recommended built-in rules + "no-var-keyword": true, + "no-parameter-reassignment": true, + "typedef": [true, "call-signature"], + + // Immutability rules + "readonly-keyword": true, + "readonly-array": true, + "no-let": true, + "no-object-mutation": true, + "no-delete": true, + "no-method-signature": true, + + // Functional style rules + "no-this": true, + "no-class": true, + "no-mixed-interface": true, + "no-expression-statement": [ + true, + { "ignore-prefix": ["console.", "process.exit"] } + ], + "no-if-statement": true + /* end tslint-immutable rules */ + } +}