diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,14 @@ task _legacyJs(type:Copy) { into distDir } +task _buildTs(dependsOn: _npmInstall, type:Exec) { + inputs.dir('src/ts') + inputs.file('tsc.json') + outputs.dir(distDir) + + commandLine 'node_modules/.bin/tsc', '-p', 'tsc.json' +} + task _packageMeta(type: Copy) { inputs.property("version", version) from('.') { @@ -45,13 +53,15 @@ task _packageMeta(type: Copy) { } } -task build(dependsOn: [_npmInstall, _legacyJs, _packageMeta]) { +task build(dependsOn: [_npmInstall, _buildTs, _legacyJs, _packageMeta]) { } task _localInstall(dependsOn: build, type: Exec) { inputs.file("$distDir/package.json") - outputs.upToDateWhen { true } + outputs.upToDateWhen { + new File("$projectDir/node_modules/@implab/core").exists() + } commandLine 'npm', 'install', '--no-save', '--force', distDir } diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -25,5 +25,6 @@ "@types/tape": "latest", "requirejs": "latest", "faucet": "latest" - } + }, + "types": "main.d.ts" } diff --git a/run-amd-tests.js b/run-amd-tests.js --- a/run-amd-tests.js +++ b/run-amd-tests.js @@ -2,6 +2,11 @@ var requirejs = require('requirejs'); requirejs.config({ baseUrl: '.', + map: { + "*": { + "@implab/core": "core" + } + }, packages: [{ name: "core", location: "build/dist" diff --git a/src/js/Uuid.js b/src/js/Uuid.js deleted file mode 100644 --- a/src/js/Uuid.js +++ /dev/null @@ -1,278 +0,0 @@ -// uuid.js -// -// Copyright (c) 2010-2012 Robert Kieffer -// MIT License - http://opensource.org/licenses/mit-license.php -define([], function () { - 'use strict'; - - var _window = 'undefined' !== typeof window ? window : null; - - // Unique ID creation requires a high quality random # generator. We - // feature - // detect to determine the best RNG source, normalizing to a function - // that - // returns 128-bits of randomness, since that's what's usually required - var _rng, _mathRNG, _nodeRNG, _whatwgRNG, _previousRoot; - - function setupBrowser() { - // Allow for MSIE11 msCrypto - var _crypto = _window.crypto || _window.msCrypto; - - if (!_rng && _crypto && _crypto.getRandomValues) { - // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto - // - // Moderately fast, high quality - try { - var _rnds8 = new Uint8Array(16); - _whatwgRNG = _rng = function whatwgRNG() { - _crypto.getRandomValues(_rnds8); - return _rnds8; - }; - _rng(); - } catch (e) { /**/ } - } - - if (!_rng) { - // Math.random()-based (RNG) - // - // If all else fails, use Math.random(). It's fast, but is of - // unspecified - // quality. - var _rnds = new Array(16); - _mathRNG = _rng = function () { - for (var i = 0, r; i < 16; i++) { - if ((i & 0x03) === 0) { - r = Math.random() * 0x100000000; - } - _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; - } - - return _rnds; - }; - if ('undefined' !== typeof console && console.warn) { - console - .warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()"); - } - } - } - - function setupNode() { - // Node.js crypto-based RNG - - // http://nodejs.org/docs/v0.6.2/api/crypto.html - // - // Moderately fast, high quality - if ('function' === typeof require) { - try { - var _rb = require('crypto').randomBytes; - _nodeRNG = _rng = _rb && function () { - return _rb(16); - }; - _rng(); - } catch (e) { /**/ } - } - } - - if (_window) { - setupBrowser(); - } else { - setupNode(); - } - - // Buffer class to use - var BufferClass = ('function' === typeof Buffer) ? Buffer : Array; - - // Maps for number <-> hex string conversion - var _byteToHex = []; - var _hexToByte = {}; - for (var i = 0; i < 256; i++) { - _byteToHex[i] = (i + 0x100).toString(16).substr(1); - _hexToByte[_byteToHex[i]] = i; - } - - // **`parse()` - Parse a UUID into it's component bytes** - function parse(s, buf, offset) { - var i = (buf && offset) || 0, - ii = 0; - - buf = buf || []; - s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) { - if (ii < 16) { // Don't overflow! - buf[i + ii++] = _hexToByte[oct]; - } - }); - - // Zero out remaining bytes if string was short - while (ii < 16) { - buf[i + ii++] = 0; - } - - return buf; - } - - // **`unparse()` - Convert UUID byte array (ala parse()) into a string** - function unparse(buf, offset) { - var i = offset || 0, - bth = _byteToHex; - return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + - bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + - bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + - bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + - bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]]; - } - - // **`v1()` - Generate time-based UUID** - // - // Inspired by https://github.com/LiosK/UUID.js - // and http://docs.python.org/library/uuid.html - - // random #'s we need to init node and clockseq - var _seedBytes = _rng(); - - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = - // 1) - var _nodeId = [ - _seedBytes[0] | 0x01, - _seedBytes[1], - _seedBytes[2], - _seedBytes[3], - _seedBytes[4], - _seedBytes[5] - ]; - - // Per 4.2.2, randomize (14 bit) clockseq - var _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; - - // Previous uuid creation time - var _lastMSecs = 0, - _lastNSecs = 0; - - // See https://github.com/broofa/node-uuid for API details - function v1(options, buf, offset) { - var i = buf && offset || 0; - var b = buf || []; - - options = options || {}; - - var clockseq = (options.clockseq != null) ? options.clockseq : _clockseq; - - // UUID timestamps are 100 nano-second units since the Gregorian - // epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and - // 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 - // 00:00. - var msecs = (options.msecs != null) ? options.msecs : new Date() - .getTime(); - - // Per 4.2.1.2, use count of uuid's generated during the current - // clock - // cycle to simulate higher resolution clock - var nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1; - - // Time since last uuid creation (in msecs) - var dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000; - - // Per 4.2.1.2, Bump clockseq on clock regression - if (dt < 0 && options.clockseq == null) { - clockseq = clockseq + 1 & 0x3fff; - } - - // Reset nsecs if clock regresses (new clockseq) or we've moved onto - // a new - // time interval - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) { - nsecs = 0; - } - - // Per 4.2.1.2 Throw error if too many uuids are requested - if (nsecs >= 10000) { - throw new Error( - 'uuid.v1(): Can\'t create more than 10M uuids/sec'); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; - - // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - msecs += 12219292800000; - - // `time_low` - var tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; - - // `time_mid` - var tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; - - // `time_high_and_version` - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - b[i++] = tmh >>> 16 & 0xff; - - // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - b[i++] = clockseq >>> 8 | 0x80; - - // `clock_seq_low` - b[i++] = clockseq & 0xff; - - // `node` - var node = options.node || _nodeId; - for (var n = 0; n < 6; n++) { - b[i + n] = node[n]; - } - - return buf ? buf : unparse(b); - } - - // **`v4()` - Generate random UUID** - - // See https://github.com/broofa/node-uuid for API details - function v4(options, buf, offset) { - // Deprecated - 'format' argument, as supported in v1.2 - var i = buf && offset || 0; - - if (typeof (options) === 'string') { - buf = (options === 'binary') ? new BufferClass(16) : null; - options = null; - } - options = options || {}; - - var rnds = options.random || (options.rng || _rng)(); - - // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - rnds[6] = (rnds[6] & 0x0f) | 0x40; - rnds[8] = (rnds[8] & 0x3f) | 0x80; - - // Copy bytes to buffer, if provided - if (buf) { - for (var ii = 0; ii < 16; ii++) { - buf[i + ii] = rnds[ii]; - } - } - - return buf || unparse(rnds); - } - - // Export public API - var uuid = function () { - return new String(v4()); - }; - uuid.v1 = v1; - uuid.v4 = v4; - uuid.create = v4; - uuid.empty = "00000000-0000-0000-0000-000000000000"; - uuid.parse = parse; - uuid.unparse = unparse; - uuid.BufferClass = BufferClass; - uuid._rng = _rng; - uuid._mathRNG = _mathRNG; - uuid._nodeRNG = _nodeRNG; - uuid._whatwgRNG = _whatwgRNG; - - return uuid; -}); \ No newline at end of file diff --git a/src/js/main.js b/src/js/main.js deleted file mode 100644 --- a/src/js/main.js +++ /dev/null @@ -1,3 +0,0 @@ -declare([], function(){ - // does nothing yet... -}); \ No newline at end of file diff --git a/src/ts/Uuid.ts b/src/ts/Uuid.ts new file mode 100644 --- /dev/null +++ b/src/ts/Uuid.ts @@ -0,0 +1,280 @@ +// Typescript port of the uuid.js +// Copyright (c) 2018 Sergey Smirnov +// BSD-2-Clause License https://opensource.org/licenses/BSD-2-Clause +// +// uuid.js +// Copyright (c) 2010-2012 Robert Kieffer +// MIT License - http://opensource.org/licenses/mit-license.php + +let _window : any = 'undefined' !== typeof window ? window : null; + +// Unique ID creation requires a high quality random # generator. We +// feature +// detect to determine the best RNG source, normalizing to a function +// that +// returns 128-bits of randomness, since that's what's usually required +let _rng; + +function setupBrowser() { + // Allow for MSIE11 msCrypto + let _crypto = _window.crypto || _window.msCrypto; + + if (!_rng && _crypto && _crypto.getRandomValues) { + // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto + // + // Moderately fast, high quality + try { + let _rnds8 = new Uint8Array(16); + _rng = function whatwgRNG() { + _crypto.getRandomValues(_rnds8); + return _rnds8; + }; + _rng(); + } catch (e) { /**/ } + } + + if (!_rng) { + // Math.random()-based (RNG) + // + // If all else fails, use Math.random(). It's fast, but is of + // unspecified + // quality. + let _rnds = new Array(16); + _rng = function () { + for (var i = 0, r; i < 16; i++) { + if ((i & 0x03) === 0) { + r = Math.random() * 0x100000000; + } + _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff; + } + + return _rnds; + }; + if ('undefined' !== typeof console && console.warn) { + console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()"); + } + } +} + +function setupNode() { + // Node.js crypto-based RNG - + // http://nodejs.org/docs/v0.6.2/api/crypto.html + // + // Moderately fast, high quality + if ('function' === typeof require) { + try { + let _rb = require('crypto').randomBytes; + _rng = _rb && function () { + return _rb(16); + }; + _rng(); + } catch (e) { /**/ } + } +} + +if (_window) { + setupBrowser(); +} else { + setupNode(); +} + +// Buffer class to use +let BufferClass = ('function' === typeof Buffer) ? Buffer : Array; + +// Maps for number <-> hex string conversion +let _byteToHex = []; +let _hexToByte = {}; +for (let i = 0; i < 256; i++) { + _byteToHex[i] = (i + 0x100).toString(16).substr(1); + _hexToByte[_byteToHex[i]] = i; +} + +// **`parse()` - Parse a UUID into it's component bytes** +function parse(s, buf?, offset?) : Array { + let i = (buf && offset) || 0, ii = 0; + + buf = buf || []; + s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) { + if (ii < 16) { // Don't overflow! + buf[i + ii++] = _hexToByte[oct]; + } + }); + + // Zero out remaining bytes if string was short + while (ii < 16) { + buf[i + ii++] = 0; + } + + return buf; +} + +// **`unparse()` - Convert UUID byte array (ala parse()) into a string** +function unparse(buf, offset?) : string { + let i = offset || 0, bth = _byteToHex; + return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' + + bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] + + bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]]; +} + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html + +// random #'s we need to init node and clockseq +let _seedBytes = _rng(); + +// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = +// 1) +let _nodeId = [ + _seedBytes[0] | 0x01, + _seedBytes[1], + _seedBytes[2], + _seedBytes[3], + _seedBytes[4], + _seedBytes[5] +]; + +// Per 4.2.2, randomize (14 bit) clockseq +let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff; + +// Previous uuid creation time +let _lastMSecs = 0, _lastNSecs = 0; + +// See https://github.com/broofa/node-uuid for API details +function v1(options?, buf?, offset?) : string { + let i = buf && offset || 0; + let b = buf || []; + + options = options || {}; + + let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq; + + // UUID timestamps are 100 nano-second units since the Gregorian + // epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and + // 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 + // 00:00. + let msecs = (options.msecs != null) ? options.msecs : new Date() + .getTime(); + + // Per 4.2.1.2, use count of uuid's generated during the current + // clock + // cycle to simulate higher resolution clock + let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1; + + // Time since last uuid creation (in msecs) + let dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000; + + // Per 4.2.1.2, Bump clockseq on clock regression + if (dt < 0 && options.clockseq == null) { + clockseq = clockseq + 1 & 0x3fff; + } + + // Reset nsecs if clock regresses (new clockseq) or we've moved onto + // a new + // time interval + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) { + nsecs = 0; + } + + // Per 4.2.1.2 Throw error if too many uuids are requested + if (nsecs >= 10000) { + throw new Error( + 'uuid.v1(): Can\'t create more than 10M uuids/sec'); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; + + // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + msecs += 12219292800000; + + // `time_low` + let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; + + // `time_mid` + let tmh = (msecs / 0x100000000 * 10000) & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; + + // `time_high_and_version` + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + b[i++] = tmh >>> 16 & 0xff; + + // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + b[i++] = clockseq >>> 8 | 0x80; + + // `clock_seq_low` + b[i++] = clockseq & 0xff; + + // `node` + let node = options.node || _nodeId; + for (let n = 0; n < 6; n++) { + b[i + n] = node[n]; + } + + return buf ? buf : unparse(b); +} + +// **`v4()` - Generate random UUID** + +// See https://github.com/broofa/node-uuid for API details +function v4(options?, buf?, offset?) : string { + // Deprecated - 'format' argument, as supported in v1.2 + let i = buf && offset || 0; + + if (typeof (options) === 'string') { + buf = (options === 'binary') ? new BufferClass(16) : null; + options = null; + } + options = options || {}; + + let rnds = options.random || (options.rng || _rng)(); + + // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + rnds[6] = (rnds[6] & 0x0f) | 0x40; + rnds[8] = (rnds[8] & 0x3f) | 0x80; + + // Copy bytes to buffer, if provided + if (buf) { + for (let ii = 0; ii < 16; ii++) { + buf[i + ii] = rnds[ii]; + } + } + + return buf || unparse(rnds); +} + +// Export public API +const empty = "00000000-0000-0000-0000-000000000000"; + +interface uuid { + (options?, buf?, offset?) : string; + v1(options?, buf?, offset?) : string; + v4(options?, buf?, offset?) : string; + readonly empty: string; + parse(s, buf?, offset?) : Array; + unparse(buf, offset?) : string; +} + +export = (() =>{ + var f : any = function(options?, buf?, offset?) : string { + return v4(options, buf, offset); + }; + f.v1 = v1; + f.v4 = v4; + f.empty = empty; + f.parse = parse; + f.unparse = unparse; + return f; +})(); \ No newline at end of file diff --git a/src/ts/main.ts b/src/ts/main.ts new file mode 100644 diff --git a/test/ts/dummy.ts b/test/ts/dummy.ts --- a/test/ts/dummy.ts +++ b/test/ts/dummy.ts @@ -1,10 +1,12 @@ import * as tape from 'tape'; +import * as uuid from '@implab/core/Uuid'; tape('simple', function(t){ t.pass("sync assert"); setTimeout(() => { t.pass("async assert"); - + t.comment(uuid()); + t.ok(uuid() != uuid()); // end should be called after the last assertion t.end(); }, 100); diff --git a/tsc.json b/tsc.json new file mode 100644 --- /dev/null +++ b/tsc.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "amd", + "sourceMap": true, + "outDir" : "build/dist", + "declaration": true + }, + "include" : [ + "src/ts/**/*.ts" + ] +} \ No newline at end of file diff --git a/tsc.test.json b/tsc.test.json --- a/tsc.test.json +++ b/tsc.test.json @@ -4,7 +4,7 @@ "module": "amd", "sourceMap": true, "outDir" : "build/test", - "allowJs": true + "moduleResolution": "node" }, "include" : [ "test/ts/**/*.ts"