@atproto/lexicon
Advanced tools
Comparing version 0.4.3 to 0.4.4
# @atproto/lexicon | ||
## 0.4.4 | ||
### Patch Changes | ||
- [#3223](https://github.com/bluesky-social/atproto/pull/3223) [`9fd65ba0f`](https://github.com/bluesky-social/atproto/commit/9fd65ba0fa4caca59fd0e6156145e4c2618e3a95) Thanks [@gaearon](https://github.com/gaearon)! - Add fast paths that skip UTF8 encoding | ||
## 0.4.3 | ||
@@ -4,0 +10,0 @@ |
@@ -182,19 +182,38 @@ "use strict"; | ||
// maxLength and minLength | ||
if (typeof def.maxLength === 'number' || typeof def.minLength === 'number') { | ||
const len = (0, common_web_1.utf8Len)(value); | ||
if (typeof def.maxLength === 'number') { | ||
if (len > def.maxLength) { | ||
return { | ||
success: false, | ||
error: new types_1.ValidationError(`${path} must not be longer than ${def.maxLength} characters`), | ||
}; | ||
} | ||
if (typeof def.minLength === 'number' || typeof def.maxLength === 'number') { | ||
// If the JavaScript string length * 3 is below the maximum limit, | ||
// its UTF8 length (which <= .length * 3) will also be below. | ||
if (typeof def.minLength === 'number' && value.length * 3 < def.minLength) { | ||
return { | ||
success: false, | ||
error: new types_1.ValidationError(`${path} must not be shorter than ${def.minLength} characters`), | ||
}; | ||
} | ||
if (typeof def.minLength === 'number') { | ||
if (len < def.minLength) { | ||
return { | ||
success: false, | ||
error: new types_1.ValidationError(`${path} must not be shorter than ${def.minLength} characters`), | ||
}; | ||
// If the JavaScript string length * 3 is within the maximum limit, | ||
// its UTF8 length (which <= .length * 3) will also be within. | ||
// When there's no minimal length, this lets us skip the UTF8 length check. | ||
let canSkipUtf8LenChecks = false; | ||
if (typeof def.minLength === 'undefined' && | ||
typeof def.maxLength === 'number' && | ||
value.length * 3 <= def.maxLength) { | ||
canSkipUtf8LenChecks = true; | ||
} | ||
if (!canSkipUtf8LenChecks) { | ||
const len = (0, common_web_1.utf8Len)(value); | ||
if (typeof def.maxLength === 'number') { | ||
if (len > def.maxLength) { | ||
return { | ||
success: false, | ||
error: new types_1.ValidationError(`${path} must not be longer than ${def.maxLength} characters`), | ||
}; | ||
} | ||
} | ||
if (typeof def.minLength === 'number') { | ||
if (len < def.minLength) { | ||
return { | ||
success: false, | ||
error: new types_1.ValidationError(`${path} must not be shorter than ${def.minLength} characters`), | ||
}; | ||
} | ||
} | ||
} | ||
@@ -201,0 +220,0 @@ } |
{ | ||
"name": "@atproto/lexicon", | ||
"version": "0.4.3", | ||
"version": "0.4.4", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "atproto Lexicon schema language library", |
@@ -201,23 +201,48 @@ import { utf8Len, graphemeLen } from '@atproto/common-web' | ||
// maxLength and minLength | ||
if (typeof def.maxLength === 'number' || typeof def.minLength === 'number') { | ||
const len = utf8Len(value) | ||
if (typeof def.minLength === 'number' || typeof def.maxLength === 'number') { | ||
// If the JavaScript string length * 3 is below the maximum limit, | ||
// its UTF8 length (which <= .length * 3) will also be below. | ||
if (typeof def.minLength === 'number' && value.length * 3 < def.minLength) { | ||
return { | ||
success: false, | ||
error: new ValidationError( | ||
`${path} must not be shorter than ${def.minLength} characters`, | ||
), | ||
} | ||
} | ||
if (typeof def.maxLength === 'number') { | ||
if (len > def.maxLength) { | ||
return { | ||
success: false, | ||
error: new ValidationError( | ||
`${path} must not be longer than ${def.maxLength} characters`, | ||
), | ||
// If the JavaScript string length * 3 is within the maximum limit, | ||
// its UTF8 length (which <= .length * 3) will also be within. | ||
// When there's no minimal length, this lets us skip the UTF8 length check. | ||
let canSkipUtf8LenChecks = false | ||
if ( | ||
typeof def.minLength === 'undefined' && | ||
typeof def.maxLength === 'number' && | ||
value.length * 3 <= def.maxLength | ||
) { | ||
canSkipUtf8LenChecks = true | ||
} | ||
if (!canSkipUtf8LenChecks) { | ||
const len = utf8Len(value) | ||
if (typeof def.maxLength === 'number') { | ||
if (len > def.maxLength) { | ||
return { | ||
success: false, | ||
error: new ValidationError( | ||
`${path} must not be longer than ${def.maxLength} characters`, | ||
), | ||
} | ||
} | ||
} | ||
} | ||
if (typeof def.minLength === 'number') { | ||
if (len < def.minLength) { | ||
return { | ||
success: false, | ||
error: new ValidationError( | ||
`${path} must not be shorter than ${def.minLength} characters`, | ||
), | ||
if (typeof def.minLength === 'number') { | ||
if (len < def.minLength) { | ||
return { | ||
success: false, | ||
error: new ValidationError( | ||
`${path} must not be shorter than ${def.minLength} characters`, | ||
), | ||
} | ||
} | ||
@@ -224,0 +249,0 @@ } |
@@ -318,2 +318,20 @@ import { LexiconDoc } from '../../src/index' | ||
lexicon: 1, | ||
id: 'com.example.stringLengthNoMinLength', | ||
defs: { | ||
main: { | ||
type: 'record', | ||
record: { | ||
type: 'object', | ||
properties: { | ||
string: { | ||
type: 'string', | ||
maxLength: 4, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
lexicon: 1, | ||
id: 'com.example.stringLengthGrapheme', | ||
@@ -320,0 +338,0 @@ defs: { |
@@ -570,16 +570,72 @@ import { CID } from 'multiformats/cid' | ||
it('Applies string length constraint', () => { | ||
// Shorter than two UTF8 characters | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '', | ||
}), | ||
).toThrow('Record/string must not be shorter than 2 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'a', | ||
}), | ||
).toThrow('Record/string must not be shorter than 2 characters') | ||
// Two to four UTF8 characters | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '123', | ||
string: 'ab', | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '\u0301', // Combining acute accent (2 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'a\u0301', // 'a' + combining acute accent (1 + 2 bytes = 3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'aé', // 'a' (1 byte) + 'é' (2 bytes) = 3 bytes | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'abc', | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '一', // CJK character (3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '\uD83D', // Unpaired high surrogate (3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'abcd', | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'éé', // 'é' + 'é' (2 + 2 bytes = 4 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'aaé', // 1 + 1 + 2 = 4 bytes | ||
}) | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '👋', // 4 bytes | ||
}) | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '1', | ||
string: 'abcde', | ||
}), | ||
).toThrow('Record/string must not be shorter than 2 characters') | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '12345', | ||
string: 'a\u0301\u0301', // 1 + (2 * 2) = 5 bytes | ||
}), | ||
@@ -590,7 +646,132 @@ ).toThrow('Record/string must not be longer than 4 characters') | ||
$type: 'com.example.stringLength', | ||
string: '👨👩👧👧', | ||
string: '\uD83D\uD83D', // Two unpaired high surrogates (3 * 2 = 6 bytes) | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: 'ééé', // 2 + 2 + 2 bytes = 6 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '👋a', // 4 + 1 bytes = 5 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '👨👨', // 4 + 4 = 8 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLength', { | ||
$type: 'com.example.stringLength', | ||
string: '👨👩👧👧', // 4 emojis × 4 bytes + 3 ZWJs × 3 bytes = 25 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
}) | ||
it('Applies string length constraint (no minLength)', () => { | ||
// Shorter than two UTF8 characters | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '', | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'a', | ||
}) | ||
// Two to four UTF8 characters | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'ab', | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '\u0301', // Combining acute accent (2 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'a\u0301', // 'a' + combining acute accent (1 + 2 bytes = 3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'aé', // 'a' (1 byte) + 'é' (2 bytes) = 3 bytes | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'abc', | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '一', // CJK character (3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '\uD83D', // Unpaired high surrogate (3 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'abcd', | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'éé', // 'é' + 'é' (2 + 2 bytes = 4 bytes) | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'aaé', // 1 + 1 + 2 = 4 bytes | ||
}) | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '👋', // 4 bytes | ||
}) | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'abcde', | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'a\u0301\u0301', // 1 + (2 * 2) = 5 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '\uD83D\uD83D', // Two unpaired high surrogates (3 * 2 = 6 bytes) | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: 'ééé', // 2 + 2 + 2 bytes = 6 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '👋a', // 4 + 1 bytes = 5 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '👨👨', // 4 + 4 = 8 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
expect(() => | ||
lex.assertValidRecord('com.example.stringLengthNoMinLength', { | ||
$type: 'com.example.stringLengthNoMinLength', | ||
string: '👨👩👧👧', // 4 emojis × 4 bytes + 3 ZWJs × 3 bytes = 25 bytes | ||
}), | ||
).toThrow('Record/string must not be longer than 4 characters') | ||
}) | ||
it('Applies grapheme string length constraint', () => { | ||
@@ -597,0 +778,0 @@ // Shorter than two graphemes |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1186162
27905