@jarenjs/core 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/ARCHITECTURE.md +800 -0
  2. package/LICENSE +21 -0
  3. package/README.md +68 -0
  4. package/dist/types/array.d.ts +28 -0
  5. package/dist/types/bigint.d.ts +5 -0
  6. package/dist/types/dates.d.ts +79 -0
  7. package/dist/types/float.d.ts +32 -0
  8. package/dist/types/function.d.ts +22 -0
  9. package/dist/types/index.d.ts +119 -0
  10. package/dist/types/integer.d.ts +24 -0
  11. package/dist/types/math/float64.d.ts +121 -0
  12. package/dist/types/math/index.d.ts +5 -0
  13. package/dist/types/math/int32.d.ts +41 -0
  14. package/dist/types/math/vec2f64.d.ts +346 -0
  15. package/dist/types/math/vec2i32.d.ts +43 -0
  16. package/dist/types/math/vec3f64.d.ts +61 -0
  17. package/dist/types/number.d.ts +39 -0
  18. package/dist/types/object.d.ts +44 -0
  19. package/dist/types/scan.d.ts +64 -0
  20. package/dist/types/string.d.ts +65 -0
  21. package/dist/types/text/base64.d.ts +4 -0
  22. package/dist/types/text/basic.d.ts +5 -0
  23. package/dist/types/text/email.d.ts +3 -0
  24. package/dist/types/text/host.d.ts +14 -0
  25. package/dist/types/text/i18n.d.ts +13 -0
  26. package/dist/types/text/identifiers.d.ts +5 -0
  27. package/dist/types/text/index.d.ts +8 -0
  28. package/dist/types/text/iregexp.d.ts +36 -0
  29. package/dist/types/text/misc.d.ts +4 -0
  30. package/dist/types/text/punycode.d.ts +86 -0
  31. package/package.json +101 -0
  32. package/src/array.js +57 -0
  33. package/src/bigint.js +30 -0
  34. package/src/dates.js +371 -0
  35. package/src/float.js +107 -0
  36. package/src/function.js +56 -0
  37. package/src/index.js +223 -0
  38. package/src/integer.js +77 -0
  39. package/src/math/float64.js +316 -0
  40. package/src/math/index.js +5 -0
  41. package/src/math/int32.js +235 -0
  42. package/src/math/vec2f64.js +706 -0
  43. package/src/math/vec2i32.js +250 -0
  44. package/src/math/vec3f64.js +225 -0
  45. package/src/number.js +63 -0
  46. package/src/object.js +240 -0
  47. package/src/scan.js +96 -0
  48. package/src/string.js +194 -0
  49. package/src/text/base64.js +54 -0
  50. package/src/text/basic.js +23 -0
  51. package/src/text/email.js +61 -0
  52. package/src/text/host.js +335 -0
  53. package/src/text/i18n.js +294 -0
  54. package/src/text/identifiers.js +27 -0
  55. package/src/text/index.js +11 -0
  56. package/src/text/iregexp.js +308 -0
  57. package/src/text/misc.js +19 -0
  58. package/src/text/punycode.js +407 -0
@@ -0,0 +1,308 @@
1
+ // I-Regexp: An Interoperable Regular Expression Format
2
+ // https://www.rfc-editor.org/rfc/rfc9485.html
3
+ //
4
+ // A complete RFC 9485 validator and translator to ECMAScript RegExp,
5
+ // used by the JSONPath engine (json/path.js) for the match()/search()
6
+ // function extensions of RFC 9535.
7
+
8
+ import {
9
+ isDigitCode,
10
+ } from '../scan.js';
11
+
12
+ // single-character escapes allowed by RFC 9485: \( \) \* \+ \- \. \? \[ \\ \] \^ \n \r \t \{ \| \}
13
+ const IREGEXP_SINGLE_ESC = '()*+-.?[\\]^nrt{|}';
14
+
15
+ // Unicode general categories allowed in \p{...} / \P{...} (RFC 9485):
16
+ // the key is the major category, the value the allowed subcategory letters
17
+ const IREGEXP_CATEGORIES = {
18
+ L: 'lmotu',
19
+ M: 'cen',
20
+ N: 'dlo',
21
+ P: 'cdefios',
22
+ Z: 'lps',
23
+ S: 'ckmo',
24
+ C: 'cfno',
25
+ };
26
+
27
+ /**
28
+ * Validate an I-Regexp (RFC 9485) against its complete ABNF grammar and
29
+ * translate it to an equivalent ECMAScript pattern (RFC 9485 section 5.3):
30
+ *
31
+ * - unescaped dots outside character classes become [^\n\r]
32
+ * - '\-' outside a character class becomes '-' (not a valid ECMAScript
33
+ * escape under the 'u' flag)
34
+ * - unescaped '^' and '$' (grammatically NormalChars) pass through
35
+ * unchanged: the RFC's own ECMAScript/PCRE/RE2/Ruby conversions
36
+ * (sections 5.3/5.4) leave them alone, which gives them anchor
37
+ * semantics, and the official JSONPath compliance test suite expects
38
+ * exactly that; write '\^' for a literal caret and '[$]' for a
39
+ * literal dollar ('\$' is not a valid I-Regexp escape)
40
+ *
41
+ * I-Regexp deliberately excludes lookaround, backreferences, lazy
42
+ * quantifiers, multi-character escapes (\d \s \w), and inline flags; per
43
+ * RFC 9535 sections 2.4.6/2.4.7 a nonconforming pattern makes
44
+ * match()/search() yield LogicalFalse, so this returns null for them.
45
+ *
46
+ * @param {string} pattern - The I-Regexp pattern
47
+ * @returns {string|null} The ECMAScript pattern source, or null when invalid
48
+ */
49
+ export function translateIRegexp(pattern) {
50
+ const n = pattern.length;
51
+ let i = 0;
52
+ let out = '';
53
+
54
+ // reads the code point at i; -1 marks a lone surrogate (not a
55
+ // Unicode scalar value, so never valid in an I-Regexp)
56
+ function codePoint() {
57
+ const c = pattern.charCodeAt(i);
58
+ if (c >= 0xD800 && c <= 0xDFFF) {
59
+ if (c >= 0xDC00 || i + 1 >= n)
60
+ return -1;
61
+ const d = pattern.charCodeAt(i + 1);
62
+ return (d >= 0xDC00 && d <= 0xDFFF) ? pattern.codePointAt(i) : -1;
63
+ }
64
+ return c;
65
+ }
66
+
67
+ function emitCodePoint(cp) {
68
+ const width = cp > 0xFFFF ? 2 : 1;
69
+ out += pattern.slice(i, i + width);
70
+ i += width;
71
+ }
72
+
73
+ // NormalChar = %x00-27 / "," / "-" / %x2F-3E / %x40-5A / %x5E-7A / %x7E-D7FF / %xE000-10FFFF
74
+ function isNormalChar(cp) {
75
+ return cp <= 0x27
76
+ || cp === 0x2C || cp === 0x2D
77
+ || (cp >= 0x2F && cp <= 0x3E)
78
+ || (cp >= 0x40 && cp <= 0x5A)
79
+ || (cp >= 0x5E && cp <= 0x7A)
80
+ || cp >= 0x7E; // codePoint() already excluded surrogates
81
+ }
82
+
83
+ // CCchar = %x00-2C / %x2E-5A / %x5E-D7FF / %xE000-10FFFF (or SingleCharEsc)
84
+ function isCCchar(cp) {
85
+ return cp !== 0x2D && cp !== 0x5B && cp !== 0x5C && cp !== 0x5D;
86
+ }
87
+
88
+ // "\" already consumed; SingleCharEsc / catEsc / complEsc
89
+ function parseEscape(inClass) {
90
+ if (i >= n)
91
+ return false;
92
+ const ch = pattern[i];
93
+ if (ch === 'p' || ch === 'P') {
94
+ i++;
95
+ if (pattern[i] !== '{')
96
+ return false;
97
+ i++;
98
+ const sub = IREGEXP_CATEGORIES[pattern[i]];
99
+ if (sub === undefined)
100
+ return false;
101
+ let prop = pattern[i];
102
+ i++;
103
+ if (pattern[i] !== '}') {
104
+ if (i >= n || !sub.includes(pattern[i]))
105
+ return false;
106
+ prop += pattern[i];
107
+ i++;
108
+ if (pattern[i] !== '}')
109
+ return false;
110
+ }
111
+ i++;
112
+ out += '\\' + ch + '{' + prop + '}';
113
+ return true;
114
+ }
115
+ if (!IREGEXP_SINGLE_ESC.includes(ch))
116
+ return false;
117
+ // '\-' is a valid I-Regexp escape but not a valid ECMAScript 'u'
118
+ // escape outside a character class
119
+ out += (ch === '-' && !inClass) ? '-' : '\\' + ch;
120
+ i++;
121
+ return true;
122
+ }
123
+
124
+ // CCE1 = ( CCchar [ "-" CCchar ] ) / charClassEsc
125
+ function parseCCE1() {
126
+ let rangeStart = false; // \p{...} cannot start a range
127
+ if (pattern.charCodeAt(i) === 0x5C) { // backslash
128
+ i++;
129
+ const isCat = pattern[i] === 'p' || pattern[i] === 'P';
130
+ if (!parseEscape(true))
131
+ return false;
132
+ rangeStart = !isCat;
133
+ }
134
+ else {
135
+ const cp = codePoint();
136
+ if (cp < 0 || !isCCchar(cp))
137
+ return false;
138
+ emitCodePoint(cp);
139
+ rangeStart = true;
140
+ }
141
+ // optional range: "-" CCchar (a trailing "-]" belongs to the class)
142
+ if (rangeStart && pattern[i] === '-' && i + 1 < n && pattern[i + 1] !== ']') {
143
+ out += '-';
144
+ i++;
145
+ if (pattern.charCodeAt(i) === 0x5C) {
146
+ i++;
147
+ return pattern[i] !== 'p' && pattern[i] !== 'P' && parseEscape(true);
148
+ }
149
+ const cp = codePoint();
150
+ if (cp < 0 || !isCCchar(cp))
151
+ return false;
152
+ emitCodePoint(cp);
153
+ }
154
+ return true;
155
+ }
156
+
157
+ // charClassExpr = "[" [ "^" ] ( "-" / CCE1 ) *CCE1 [ "-" ] "]"
158
+ // (with the extra RFC 9485 restriction that "[^]" is not allowed)
159
+ function parseCharClassExpr() {
160
+ out += '[';
161
+ i++; // consume '['
162
+ if (pattern[i] === '^') {
163
+ out += '^';
164
+ i++;
165
+ }
166
+ if (pattern[i] === '-') {
167
+ out += '\\-';
168
+ i++;
169
+ }
170
+ else if (i >= n || pattern[i] === ']' || !parseCCE1()) {
171
+ return false;
172
+ }
173
+ for (;;) {
174
+ if (i >= n)
175
+ return false;
176
+ const ch = pattern[i];
177
+ if (ch === ']') {
178
+ out += ']';
179
+ i++;
180
+ return true;
181
+ }
182
+ if (ch === '-') { // only valid as the trailing "-]"
183
+ if (pattern[i + 1] !== ']')
184
+ return false;
185
+ out += '\\-]';
186
+ i += 2;
187
+ return true;
188
+ }
189
+ if (!parseCCE1())
190
+ return false;
191
+ }
192
+ }
193
+
194
+ // atom = NormalChar / charClass / ( "(" i-regexp ")" )
195
+ function parseAtom() {
196
+ const ch = pattern[i];
197
+ if (ch === '(') {
198
+ out += '(';
199
+ i++;
200
+ if (!parseAlternation())
201
+ return false;
202
+ if (pattern[i] !== ')')
203
+ return false;
204
+ out += ')';
205
+ i++;
206
+ return true;
207
+ }
208
+ if (ch === '.') { // matches any character except \n and \r
209
+ out += '[^\\n\\r]';
210
+ i++;
211
+ return true;
212
+ }
213
+ if (ch === '\\') {
214
+ i++;
215
+ return parseEscape(false);
216
+ }
217
+ if (ch === '[')
218
+ return parseCharClassExpr();
219
+ const cp = codePoint();
220
+ if (cp < 0 || !isNormalChar(cp))
221
+ return false;
222
+ emitCodePoint(cp);
223
+ return true;
224
+ }
225
+
226
+ // piece = atom [ quantifier ]
227
+ function parsePiece() {
228
+ if (!parseAtom())
229
+ return false;
230
+ const ch = pattern[i];
231
+ if (ch === '*' || ch === '+' || ch === '?') {
232
+ out += ch;
233
+ i++;
234
+ }
235
+ else if (ch === '{') { // range-quantifier = "{" QuantExact [ "," [ QuantExact ] ] "}"
236
+ let j = i + 1;
237
+ const first = j;
238
+ while (j < n && isDigitCode(pattern.charCodeAt(j)))
239
+ j++;
240
+ if (j === first)
241
+ return false;
242
+ if (pattern[j] === ',') {
243
+ j++;
244
+ while (j < n && isDigitCode(pattern.charCodeAt(j)))
245
+ j++;
246
+ }
247
+ if (pattern[j] !== '}')
248
+ return false;
249
+ out += pattern.slice(i, j + 1);
250
+ i = j + 1;
251
+ }
252
+ return true;
253
+ }
254
+
255
+ // branch = *piece
256
+ function parseBranch() {
257
+ while (i < n) {
258
+ const ch = pattern[i];
259
+ if (ch === '|' || ch === ')')
260
+ return true;
261
+ if (!parsePiece())
262
+ return false;
263
+ }
264
+ return true;
265
+ }
266
+
267
+ // i-regexp = branch *( "|" branch )
268
+ function parseAlternation() {
269
+ if (!parseBranch())
270
+ return false;
271
+ while (pattern[i] === '|') {
272
+ out += '|';
273
+ i++;
274
+ if (!parseBranch())
275
+ return false;
276
+ }
277
+ return true;
278
+ }
279
+
280
+ return (parseAlternation() && i === n) ? out : null;
281
+ }
282
+
283
+ /**
284
+ * Compile an I-Regexp into an ECMAScript RegExp.
285
+ * @param {string} pattern - The I-Regexp pattern
286
+ * @param {boolean} [fullMatch=false] - Anchor the whole pattern (match() semantics, true) or leave it free (search() semantics, false)
287
+ * @returns {RegExp|null} null when the pattern is not a valid I-Regexp
288
+ */
289
+ export function compileIRegexp(pattern, fullMatch = false) {
290
+ const translated = translateIRegexp(pattern);
291
+ if (translated === null)
292
+ return null;
293
+ try {
294
+ return new RegExp(fullMatch ? `^(?:${translated})$` : translated, 'u');
295
+ }
296
+ catch {
297
+ return null;
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Validates a pattern against the complete I-Regexp (RFC 9485) grammar.
303
+ * @param {string} pattern - The I-Regexp pattern to validate
304
+ * @returns {boolean} True when the pattern is a valid I-Regexp
305
+ */
306
+ export function isValidIRegexp(pattern) {
307
+ return typeof pattern === 'string' && translateIRegexp(pattern) !== null;
308
+ }
@@ -0,0 +1,19 @@
1
+ const CONST_REGPEXP_ISBN10 = /^(?:ISBN(?:-10)?:?\ *((?=\d{1,5}([ -]?)\d{1,7}\2?\d{1,6}\2?\d)(?:\d\2*){9}[\dX]))$/i;
2
+ export function isValidISBN10(str) {
3
+ return CONST_REGPEXP_ISBN10.test(str);
4
+ }
5
+
6
+ const CONST_REGEXP_ISBN13 = /^(?:ISBN(?:-13)?:?\ *(97(?:8|9)([ -]?)(?=\d{1,5}\2?\d{1,7}\2?\d{1,6}\2?\d)(?:\d\2*){9}\d))$/i;
7
+ export function isValidISBN13(str) {
8
+ return CONST_REGEXP_ISBN13.test(str);
9
+ }
10
+
11
+ const CONST_REGEXP_COUNTRY_ALPHA2 = /^(AF|AX|AL|DZ|AS|AD|AO|AI|AQ|AG|AR|AM|AW|AU|AT|AZ|BS|BH|BD|BB|BY|BE|BZ|BJ|BM|BT|BO|BQ|BA|BW|BV|BR|IO|BN|BG|BF|BI|KH|CM|CA|CV|KY|CF|TD|CL|CN|CX|CC|CO|KM|CG|CD|CK|CR|CI|HR|CU|CW|CY|CZ|DK|DJ|DM|DO|EC|EG|SV|GQ|ER|EE|ET|FK|FO|FJ|FI|FR|GF|PF|TF|GA|GM|GE|DE|GH|GI|GR|GL|GD|GP|GU|GT|GG|GN|GW|GY|HT|HM|VA|HN|HK|HU|IS|IN|ID|IR|IQ|IE|IM|IL|IT|JM|JP|JE|JO|KZ|KE|KI|KP|KR|KW|KG|LA|LV|LB|LS|LR|LY|LI|LT|LU|MO|MK|MG|MW|MY|MV|ML|MT|MH|MQ|MR|MU|YT|MX|FM|MD|MC|MN|ME|MS|MA|MZ|MM|NA|NR|NP|NL|NC|NZ|NI|NE|NG|NU|NF|MP|NO|OM|PK|PW|PS|PA|PG|PY|PE|PH|PN|PL|PT|PR|QA|RE|RO|RU|RW|BL|SH|KN|LC|MF|PM|VC|WS|SM|ST|SA|SN|RS|SC|SL|SG|SX|SK|SI|SB|SO|ZA|GS|SS|ES|LK|SD|SR|SJ|SZ|SE|CH|SY|TW|TJ|TZ|TH|TL|TG|TK|TO|TT|TN|TR|TM|TC|TV|UG|UA|AE|GB|US|UM|UY|UZ|VU|VE|VN|VG|VI|WF|EH|YE|ZM|ZW|XK)/i;
12
+ export function isValidCountryAlpha2(str) {
13
+ return CONST_REGEXP_COUNTRY_ALPHA2.test(str);
14
+ }
15
+
16
+ const CONST_REGEXP_IBAN = /^([A-Z]{2}[ '+'\\\\'+'-]?[0-9]{2})(?=(?:[ '+'\\\\'+'-]?[A-Z0-9]){9,30}\$)((?:[ '+'\\\\'+'-]?[A-Z0-9]{3,5}){2,7})([ '+'\\\\'+'-]?[A-Z0-9]{1,3})?\$/i;
17
+ export function isValidIBAN(str) {
18
+ return CONST_REGEXP_IBAN.test(str);
19
+ }