Source

revision-identifier.js

// Copyright (c) 2019 datagraph gmbh


// orignally used
// https://github.com/kelektiv/node-uuid
// via
// import * as $uuid from 'https://wzrd.in/standalone/uuid%2Fv1@latest';
// but that server was not reliable
import * as $uuid from '/javascripts/vendor/uuid-v1.js';

//var sha1Lib = require('js-sha1');
//var uuidLib = require('uuid/v1');

var UUIDStateEnum = {
  insert: 0x00,
  delete: 0x80,
};

var RSIDTypeEnum = {sha1: 1, sha256: 2};
var RSIDType = RSIDTypeEnum.sha1;
var RSIDLength = 20;
var IDNode = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06];

/**
 Encode a ZUUID given a millisecond timestamp and a buffer.
 The current time is used as the default timestamp.
 Given a null buffer, return a hex-string-encoded UUD.
 @param {Object} options
 @param {number} options.msec - the Date value to encode as millisecnds
 @param {(Array|null)} buffer - an array into which to store the UUID
 */
export function makeUUID({msecs = (new Date().getTime())} = {}, buffer = []) {
  //if (msecs === undefined) { msecs = new Date().getTime(); }
  var uuid = $uuid.v1({node: IDNode, msecs: msecs}, buffer);
  return( uuid );
}
/**
 Return a hex-string-encoded version 1 UUID for the current time.
 */
export function makeUUIDString() {
  var msecs = new Date().getTime();
  return ($uuid.v1({node: IDNode, msecs: msecs}, null));
}

/**
 Copy a UUID
 @param {Array} uuid
 */
export function copyUUID(uuid) {
  return( uuid.slice() );
}

// comparisons
// https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript/14853974
// Warn if overriding existing method
if(Array.prototype.equals)
    console.warn("Overriding existing Array.prototype.equals. Possible causes: New API defines the method, there's a framework conflict or you've got double inclusions in your code.");
// attach the .equals method to Array's prototype to call it on any array
/**
 Define equals for arrays
 */
Array.prototype.equals = function (array) {
    // if the other array is a falsy value, return
    if (!array)
        return false;

    // compare lengths - can save a lot of time 
    if (this.length != array.length)
        return false;

    for (var i = 0, l=this.length; i < l; i++) {
        // Check if we have nested arrays
        if (this[i] instanceof Array && array[i] instanceof Array) {
            // recurse into the nested arrays
            if (!this[i].equals(array[i]))
                return false;       
        }           
        else if (this[i] != array[i]) { 
            // Warning - two different object instances will never be equal: {x:20} != {x:20}
            return false;   
        }           
    }       
    return true;
}
// Hide method from for-in loops
Object.defineProperty(Array.prototype, "equals", {enumerable: false});

/**
 Return (UUIDStateEnum.delete : UUIDStateEnum.insert ) as oer the UUID state.
 @param {Array} uuid
 */
function UUIDState(uuid) {
  return( (uuid[6] & 0x80) ? UUIDStateEnum.delete : UUIDStateEnum.insert );
}

// https://github.com/krassif/node-uuid/commit/a9aac0e56f6c78b80454e9686e2eebcbbf724d45
/**
 Extract the uuid timestamp
 */
function UUIDTimestamp(b) {
    var msec = 0, nsec = 0;
    var i = 0;

    // inspect version at offset 6
    if ((b[i+6]&0x10)!=0x10) {
      throw new Error("uuid version 1 expected"); }

    // 'time_low'
    var tl = 0;
    tl |= ( b[i++] & 0xff ) << 24;
    tl |= ( b[i++] & 0xff ) << 16;
    tl |= ( b[i++] & 0xff ) << 8;
    tl |=   b[i++] & 0xff ;

      // `time_mid`
      var tmh = 0;
      tmh |= ( b[i++] & 0xff ) << 8;
      tmh |=   b[i++] & 0xff;

      // `time_high_minus_version`
      tmh |= ( b[i++] & 0xf ) << 24; 
      tmh |= ( b[i++] & 0xff ) << 16;

      // account for the sign bit
      msec = 1.0 * ( ( tl >>> 1 ) * 2 + ( ( tl & 0x7fffffff ) % 2 ) ) / 10000.0;
      msec += 1.0 * ( ( tmh >>> 1 ) * 2 + ( ( tmh & 0x7fffffff ) % 2 ) ) * 0x100000000 / 10000.0;
      
      // Per 4.1.4 - Convert from Gregorian epoch to unix epoch
    msec -= 12219292800000;
      
    // getting the nsec. they are not needed now though 
    // nsec = ( tl & 0xfffffff ) % 10000;


    return msec;
}

/**
 @param {Array} uuid
 @param {UUIDStateEnun} state
 */
export function setUUIDState(uuid, state) {
  switch(state) {
    case UUIDStateEnum.insert : uuid[6] = (uuid[6] & 0x7f); break;
    case UUIDStateEnum.delete : uuid[6] = (uuid[6] | 0x80); break;
  }
  return( uuid );
}

/**
 */
export function resetUUIDState(uuid) {
  setUUIDState(uuid, UUIDStateEnum.insert);
}

/**
 */
export function isInsertUUID(uuid) {
  return( UUIDState(uuid) == UUIDStateEnum.insert );
}

/**
 */
export function isDeleteUUID(uuid) {
  return( UUIDState(uuid) == UUIDStateEnum.delete );
}

/**
 */
export function formatUUID(uuid) {
  var isInsert = isInsertUUID(uuid);
  var cleanUUID = setUUIDState(copyUUID(uuid), UUIDStateEnum.insert);
  var timestamp = UUIDTimestamp(uuid);
  var dateTime = new Date(timestamp);
  return( `${cleanUUID}${isInsert ? '+' : '-'}${dateTime.toISOString()}` );
}