Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@
},
"dependencies": {
"base64-js": "^1.3.0",
"fast-deep-equal": "^2.0.1",
"uuid": "^8.0.0"
"fast-deep-equal": "^2.0.1"
},
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions src/AnonymousContextProcessor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { v1: uuidv1 } = require('uuid');
const { randomUuidV4 } = require('./uuid');
const { getContextKinds } = require('./context');

const errors = require('./errors');
Expand Down Expand Up @@ -57,7 +57,7 @@ function AnonymousContextProcessor(persistentStorage) {
context.key = cachedId;
return context;
} else {
const id = uuidv1();
const id = randomUuidV4();
context.key = id;
return setCachedContextKey(id, kind).then(() => context);
}
Expand Down
4 changes: 2 additions & 2 deletions src/EventSender.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const errors = require('./errors');
const utils = require('./utils');
const { v1: uuidv1 } = require('uuid');
const { randomUuidV4 } = require('./uuid');
const { getLDHeaders, transformHeaders } = require('./headers');

function EventSender(platform, environmentId, options) {
Expand All @@ -25,7 +25,7 @@ function EventSender(platform, environmentId, options) {
}

const jsonBody = JSON.stringify(events);
const payloadId = isDiagnostic ? null : uuidv1();
const payloadId = isDiagnostic ? null : randomUuidV4();

function doPostRequest(canRetry) {
const headers = isDiagnostic
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/diagnosticEvents-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ describe('DiagnosticId', () => {
expect(id1.diagnosticId).not.toEqual(id2.diagnosticId);
});

it('generates valid UUID v4 format', () => {
const id = DiagnosticId('key');
const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
expect(id.diagnosticId).toMatch(uuidV4Regex);
});

it('uses only last 6 characters of key', () => {
const id = DiagnosticId('0123456789abcdef');
expect(id.sdkKeySuffix).toEqual('abcdef');
Expand Down
7 changes: 2 additions & 5 deletions src/diagnosticEvents.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
const { v1: uuidv1 } = require('uuid');
// Note that in the diagnostic events spec, these IDs are to be generated with UUID v4. However,
// in JS we were already using v1 for unique context keys, so to avoid bringing in two packages we
// will use v1 here as well.
const { randomUuidV4 } = require('./uuid');

const { baseOptionDefs } = require('./configuration');
const messages = require('./messages');
const { appendUrlPath } = require('./utils');

function DiagnosticId(sdkKey) {
const ret = {
diagnosticId: uuidv1(),
diagnosticId: randomUuidV4(),
};
if (sdkKey) {
ret.sdkKeySuffix = sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey;
Expand Down
70 changes: 70 additions & 0 deletions src/uuid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/* global crypto */
// The implementation in this file generates UUIDs in v4 format and is suitable
// for use as a UUID in LaunchDarkly events. It is not a rigorous implementation.
//
// Adapted from:
// https://github.com/launchdarkly/js-core/blob/main/packages/sdk/browser/src/platform/randomUuidV4.ts

// It uses crypto.randomUUID when available.
// If crypto.randomUUID is not available, then it uses random values and forms
// the UUID itself.
// When possible it uses crypto.getRandomValues, but it can use Math.random
// if crypto.getRandomValues is not available.

// UUIDv4 Struct definition.
// https://www.rfc-archive.org/getrfc.php?rfc=4122
// Appendix A. Appendix A - Sample Implementation
const timeLow = { start: 0, end: 3 };
const timeMid = { start: 4, end: 5 };
const timeHiAndVersion = { start: 6, end: 7 };
const clockSeqHiAndReserved = { start: 8, end: 8 };
const clockSeqLow = { start: 9, end: 9 };
const nodes = { start: 10, end: 15 };

function getRandom128bit() {
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
const typedArray = new Uint8Array(16);
crypto.getRandomValues(typedArray);
return [...typedArray.values()];
}
const values = [];
for (let index = 0; index < 16; index += 1) {
values.push(Math.floor(Math.random() * 256));
}
return values;
}

function hex(bytes, range) {
let strVal = '';
for (let index = range.start; index <= range.end; index += 1) {
strVal += bytes[index].toString(16).padStart(2, '0');
}
return strVal;
}

function formatDataAsUuidV4(bytes) {
// eslint-disable-next-line no-bitwise, no-param-reassign
bytes[clockSeqHiAndReserved.start] = (bytes[clockSeqHiAndReserved.start] | 0x80) & 0xbf;
// eslint-disable-next-line no-bitwise, no-param-reassign
bytes[timeHiAndVersion.start] = (bytes[timeHiAndVersion.start] & 0x0f) | 0x40;

return (
`${hex(bytes, timeLow)}-${hex(bytes, timeMid)}-${hex(bytes, timeHiAndVersion)}-` +
`${hex(bytes, clockSeqHiAndReserved)}${hex(bytes, clockSeqLow)}-${hex(bytes, nodes)}`
);
}

function fallbackUuidV4() {
const bytes = getRandom128bit();
return formatDataAsUuidV4(bytes);
}

function randomUuidV4() {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}

return fallbackUuidV4();
}

module.exports = { randomUuidV4, fallbackUuidV4, formatDataAsUuidV4 };
Loading