Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(saslprep): provide a browser-compatible version of saslprep package that doesn't use zlib #202

Merged
merged 1 commit into from
Jan 15, 2024
Merged
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
16 changes: 10 additions & 6 deletions packages/saslprep/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"main": "dist/node.js",
"bugs": {
"url": "https://jira.mongodb.org/projects/COMPASS/issues",
"email": "[email protected]"
Expand All @@ -28,18 +28,22 @@
],
"license": "MIT",
"exports": {
"browser": {
"types": "./dist/browser.d.ts",
"default": "./dist/browser.js"
},
"import": {
"types": "./dist/index.d.ts",
"types": "./dist/node.d.ts",
"default": "./dist/.esm-wrapper.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
"types": "./dist/node.d.ts",
"default": "./dist/node.js"
}
},
"types": "./dist/index.d.ts",
"types": "./dist/node.d.ts",
"scripts": {
"gen-code-points": "ts-node src/generate-code-points.ts src/code-points-data.ts",
"gen-code-points": "ts-node src/generate-code-points.ts src/code-points-data.ts src/code-points-data-browser.ts",
"bootstrap": "npm run compile",
"prepublishOnly": "npm run compile",
"compile": "npm run gen-code-points && tsc -p tsconfig.json && gen-esm-wrapper . ./dist/.esm-wrapper.mjs",
Expand Down
77 changes: 77 additions & 0 deletions packages/saslprep/src/browser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import saslprep from './browser';
import { expect } from 'chai';

const chr = String.fromCodePoint;

describe('saslprep (browser)', function () {
it('should work with latin letters', function () {
const str = 'user';
expect(saslprep(str)).to.equal(str);
});

it('should work be case preserved', function () {
const str = 'USER';
expect(saslprep(str)).to.equal(str);
});

it('should work with high code points (> U+FFFF)', function () {
const str = '\uD83D\uDE00';
expect(saslprep(str, { allowUnassigned: true })).to.equal(str);
});

it('should remove `mapped to nothing` characters', function () {
expect(saslprep('I\u00ADX')).to.equal('IX');
});

it('should replace `Non-ASCII space characters` with space', function () {
expect(saslprep('a\u00A0b')).to.equal('a\u0020b');
});

it('should normalize as NFKC', function () {
expect(saslprep('\u00AA')).to.equal('a');
expect(saslprep('\u2168')).to.equal('IX');
});

it('should throws when prohibited characters', function () {
// C.2.1 ASCII control characters
expect(() => saslprep('a\u007Fb')).to.throw();

// C.2.2 Non-ASCII control characters
expect(() => saslprep('a\u06DDb')).to.throw();

// C.3 Private use
expect(() => saslprep('a\uE000b')).to.throw();

// C.4 Non-character code points
expect(() => saslprep(`a${chr(0x1fffe)}b`)).to.throw();

// C.5 Surrogate codes
expect(() => saslprep('a\uD800b')).to.throw();

// C.6 Inappropriate for plain text
expect(() => saslprep('a\uFFF9b')).to.throw();

// C.7 Inappropriate for canonical representation
expect(() => saslprep('a\u2FF0b')).to.throw();

// C.8 Change display properties or are deprecated
expect(() => saslprep('a\u200Eb')).to.throw();

// C.9 Tagging characters
expect(() => saslprep(`a${chr(0xe0001)}b`)).to.throw();
});

it('should not containt RandALCat and LCat bidi', function () {
expect(() => saslprep('a\u06DD\u00AAb')).to.throw();
});

it('RandALCat should be first and last', function () {
expect(() => saslprep('\u0627\u0031\u0628')).not.to.throw();
expect(() => saslprep('\u0627\u0031')).to.throw();
});

it('should handle unassigned code points', function () {
expect(() => saslprep('a\u0487')).to.throw();
expect(() => saslprep('a\u0487', { allowUnassigned: true })).not.to.throw();
});
});
11 changes: 11 additions & 0 deletions packages/saslprep/src/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import _saslprep from './index';
import { createMemoryCodePoints } from './memory-code-points';
import data from './code-points-data-browser';

const codePoints = createMemoryCodePoints(data);

const saslprep = _saslprep.bind(null, codePoints);

Object.assign(saslprep, { saslprep, default: saslprep });

export = saslprep;
5 changes: 5 additions & 0 deletions packages/saslprep/src/code-points-data-browser.ts

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions packages/saslprep/src/generate-code-points.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@ export default gunzipSync(
);
`
);

const fsStreamUncompressedData = createWriteStream(process.argv[3]);

fsStreamUncompressedData.write(
`const data = Buffer.from('${Buffer.concat(memory).toString(
'base64'
)}', 'base64');\nexport default data;\n`
);
45 changes: 23 additions & 22 deletions packages/saslprep/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,4 @@
import {
unassigned_code_points,
commonly_mapped_to_nothing,
non_ASCII_space_characters,
prohibited_characters,
bidirectional_r_al,
bidirectional_l,
} from './memory-code-points';

// 2.1. Mapping

/**
* non-ASCII space characters [StringPrep, C.1.2] that can be
* mapped to SPACE (U+0020)
*/
const mapping2space = non_ASCII_space_characters;

/**
* the "commonly mapped to nothing" characters [StringPrep, B.1]
* that can be mapped to nothing.
*/
const mapping2nothing = commonly_mapped_to_nothing;
import type { createMemoryCodePoints } from './memory-code-points';

// utils
const getCodePoint = (character: string) => character.codePointAt(0);
Expand Down Expand Up @@ -58,9 +37,31 @@ function toCodePoints(input: string): number[] {
* SASLprep.
*/
function saslprep(
{
unassigned_code_points,
commonly_mapped_to_nothing,
non_ASCII_space_characters,
prohibited_characters,
bidirectional_r_al,
bidirectional_l,
}: ReturnType<typeof createMemoryCodePoints>,
input: string,
opts: { allowUnassigned?: boolean } = {}
): string {
// 2.1. Mapping

/**
* non-ASCII space characters [StringPrep, C.1.2] that can be
* mapped to SPACE (U+0020)
*/
const mapping2space = non_ASCII_space_characters;

/**
* the "commonly mapped to nothing" characters [StringPrep, B.1]
* that can be mapped to nothing.
*/
const mapping2nothing = commonly_mapped_to_nothing;

if (typeof input !== 'string') {
throw new TypeError('Expected string.');
}
Expand Down
46 changes: 28 additions & 18 deletions packages/saslprep/src/memory-code-points.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import bitfield from 'sparse-bitfield';
import memory from './code-points-data';

let offset = 0;
export function createMemoryCodePoints(data: Buffer) {
let offset = 0;

/**
* Loads each code points sequence from buffer.
*/
function read(): bitfield.BitFieldInstance {
const size = memory.readUInt32BE(offset);
offset += 4;
/**
* Loads each code points sequence from buffer.
*/
function read(): bitfield.BitFieldInstance {
const size = data.readUInt32BE(offset);
offset += 4;

const codepoints = memory.slice(offset, offset + size);
offset += size;
const codepoints = data.slice(offset, offset + size);
offset += size;

return bitfield({ buffer: codepoints });
}
return bitfield({ buffer: codepoints });
}

const unassigned_code_points = read();
const commonly_mapped_to_nothing = read();
const non_ASCII_space_characters = read();
const prohibited_characters = read();
const bidirectional_r_al = read();
const bidirectional_l = read();

export const unassigned_code_points = read();
export const commonly_mapped_to_nothing = read();
export const non_ASCII_space_characters = read();
export const prohibited_characters = read();
export const bidirectional_r_al = read();
export const bidirectional_l = read();
return {
unassigned_code_points,
commonly_mapped_to_nothing,
non_ASCII_space_characters,
prohibited_characters,
bidirectional_r_al,
bidirectional_l,
};
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import saslprep from './index';
import saslprep from './node';
import { expect } from 'chai';

const chr = String.fromCodePoint;

describe('saslprep', function () {
describe('saslprep (node)', function () {
it('should work with latin letters', function () {
const str = 'user';
expect(saslprep(str)).to.equal(str);
Expand Down
11 changes: 11 additions & 0 deletions packages/saslprep/src/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import _saslprep from './index';
import { createMemoryCodePoints } from './memory-code-points';
import data from './code-points-data';

const codePoints = createMemoryCodePoints(data);

const saslprep = _saslprep.bind(null, codePoints);

Object.assign(saslprep, { saslprep, default: saslprep });

export = saslprep;
Loading