@valkyriestudios/utils 12.48.0 → 12.49.0

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.
@@ -2,14 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.convertToDate = convertToDate;
4
4
  exports.default = convertToDate;
5
- const is_1 = require("./is");
6
5
  function convertToDate(val) {
7
- if ((0, is_1.isDate)(val)) {
8
- return val;
6
+ switch (typeof val) {
7
+ case 'number': {
8
+ if (val !== val)
9
+ return null;
10
+ const d = new Date(val);
11
+ return d.getTime() === d.getTime() ? d : null;
12
+ }
13
+ case 'object':
14
+ return val instanceof Date && val.getTime() === val.getTime() ? val : null;
15
+ case 'string': {
16
+ const d = new Date(val);
17
+ return d.getTime() === d.getTime() ? d : null;
18
+ }
19
+ default:
20
+ return null;
9
21
  }
10
- else if (typeof val === 'string') {
11
- const date = new Date(val);
12
- return Number.isNaN(date.getTime()) ? null : date;
13
- }
14
- return null;
15
22
  }
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.format = format;
7
7
  exports.default = format;
8
8
  const convertToDate_1 = require("./convertToDate");
9
- const isFormat_1 = require("./isFormat");
10
9
  const LRU_1 = __importDefault(require("../caching/LRU"));
11
10
  const WEEK_STARTS = {
12
11
  mon: 'mon',
@@ -16,7 +15,6 @@ const WEEK_STARTS = {
16
15
  let DEFAULT_LOCALE = 'en-US';
17
16
  let DEFAULT_TZ = Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone || 'UTC';
18
17
  let DEFAULT_SOW = 'mon';
19
- const ESCAPE_RGX = /\[([^\]]*)]/g;
20
18
  const intl_formatters = new LRU_1.default({ max_size: 100 });
21
19
  const spec_cache = new LRU_1.default({ max_size: 100 });
22
20
  const zone_offset_cache = new LRU_1.default({ max_size: 100 });
@@ -42,15 +40,8 @@ function WeekNr(d, sow) {
42
40
  }
43
41
  }
44
42
  function toZone(d, zone) {
45
- const year = d.getUTCFullYear();
46
- const month = d.getUTCMonth();
47
- const day = d.getUTCDate();
48
43
  const time = d.getTime();
49
- const daysInMonths = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? isFormat_1.MONTHS_LEAP : isFormat_1.MONTHS;
50
- let doy = day;
51
- for (let i = 0; i <= month; i++)
52
- doy += daysInMonths[i];
53
- const ckey = zone + ':' + year + ':' + doy;
44
+ const ckey = zone + ':' + Math.floor(time / 900000);
54
45
  const cached = zone_offset_cache.get(ckey);
55
46
  if (cached !== undefined)
56
47
  return new Date(time + cached);
@@ -80,54 +71,54 @@ function runIntl(loc, token, props, val) {
80
71
  return formatter.format(val);
81
72
  }
82
73
  const Tokens = [
83
- ['YYYY', d => d.getFullYear()],
84
- ['Q', d => ((d.getMonth() + 3) / 3) | 0],
74
+ ['YYYY', d => d.getFullYear().toString()],
75
+ ['Q', d => (((d.getMonth() + 3) / 3) | 0).toString()],
85
76
  ['MMMM', (d, loc) => runIntl(loc, 'MMMM', { month: 'long' }, d)],
86
77
  ['MMM', (d, loc) => runIntl(loc, 'MMM', { month: 'short' }, d)],
87
78
  ['MM', d => {
88
79
  const val = d.getMonth() + 1;
89
80
  return (val < 10 ? '0' : '') + val;
90
81
  }],
91
- ['M', d => d.getMonth() + 1],
82
+ ['M', d => (d.getMonth() + 1).toString()],
92
83
  ['WW', (d, loc, sow) => {
93
84
  const val = WeekNr(d, sow);
94
85
  return (val < 10 ? '0' : '') + val;
95
86
  }],
96
- ['W', (d, loc, sow) => WeekNr(d, sow)],
87
+ ['W', (d, loc, sow) => WeekNr(d, sow).toString()],
97
88
  ['DD', d => {
98
89
  const val = d.getDate();
99
90
  return (val < 10 ? '0' : '') + val;
100
91
  }],
101
- ['D', d => d.getDate()],
92
+ ['D', d => d.getDate().toString()],
102
93
  ['dddd', (d, loc) => runIntl(loc, 'dddd', { weekday: 'long' }, d)],
103
94
  ['ddd', (d, loc) => runIntl(loc, 'ddd', { weekday: 'short' }, d)],
104
95
  ['HH', d => {
105
96
  const val = d.getHours();
106
97
  return (val < 10 ? '0' : '') + val;
107
98
  }],
108
- ['H', d => d.getHours()],
99
+ ['H', d => d.getHours().toString()],
109
100
  ['hh', d => {
110
101
  const val = ((d.getHours() + 11) % 12) + 1;
111
102
  return (val < 10 ? '0' : '') + val;
112
103
  }],
113
- ['h', d => ((d.getHours() + 11) % 12) + 1],
104
+ ['h', d => (((d.getHours() + 11) % 12) + 1).toString()],
114
105
  ['mm', d => {
115
106
  const val = d.getMinutes();
116
107
  return (val < 10 ? '0' : '') + val;
117
108
  }],
118
- ['m', d => d.getMinutes()],
109
+ ['m', d => d.getMinutes().toString()],
119
110
  ['ss', d => {
120
111
  const val = d.getSeconds();
121
112
  return (val < 10 ? '0' : '') + val;
122
113
  }],
123
- ['s', d => d.getSeconds()],
114
+ ['s', d => d.getSeconds().toString()],
124
115
  ['SSS', d => {
125
116
  const val = d.getMilliseconds();
126
117
  return val < 10
127
118
  ? '00' + val
128
119
  : val < 100
129
120
  ? '0' + val
130
- : val;
121
+ : val.toString();
131
122
  }],
132
123
  ['A', d => d.getHours() < 12 ? 'AM' : 'PM'],
133
124
  ['a', d => d.getHours() < 12 ? 'am' : 'pm'],
@@ -141,38 +132,35 @@ const Tokens = [
141
132
  const token_map = {};
142
133
  for (const t of Tokens)
143
134
  token_map[t[0]] = t;
144
- const TOKENS_RGX = new RegExp(Tokens.map(([tok]) => tok).join('|'), 'g');
135
+ const PARSE_RGX = new RegExp('\\[[^\\]]*\\]|' + Tokens.map(([tok]) => tok).join('|'), 'g');
145
136
  function getSpecChain(spec) {
146
137
  const cached = spec_cache.get(spec);
147
138
  if (cached !== undefined)
148
139
  return cached;
149
- let base = spec;
150
- const repl = [];
151
- let repl_len = 0;
152
- if (base.indexOf('[') >= 0) {
153
- base = base.replace(ESCAPE_RGX, (_, inner) => {
154
- const escape_token = '$' + repl_len++ + '$';
155
- repl.push([escape_token, inner]);
156
- return escape_token;
157
- });
158
- }
159
- TOKENS_RGX.lastIndex = 0;
160
140
  const parts = [];
141
+ PARSE_RGX.lastIndex = 0;
161
142
  let last_idx = 0;
162
143
  let has_token = false;
163
144
  let m;
164
- while (m = TOKENS_RGX.exec(base)) {
165
- if (m.index > last_idx) {
166
- parts.push({ literal: base.slice(last_idx, m.index) });
145
+ while (m = PARSE_RGX.exec(spec)) {
146
+ const match = m[0];
147
+ const match_start = m.index;
148
+ if (match_start > last_idx) {
149
+ parts.push(spec.slice(last_idx, match_start));
150
+ }
151
+ if (match.charCodeAt(0) === 91) {
152
+ parts.push(match.slice(1, -1));
167
153
  }
168
- parts.push({ token: token_map[m[0]] });
169
- has_token = true;
170
- last_idx = m.index + m[0].length;
154
+ else {
155
+ parts.push(token_map[match][1]);
156
+ has_token = true;
157
+ }
158
+ last_idx = match_start + match.length;
171
159
  }
172
- if (last_idx < base.length) {
173
- parts.push({ literal: base.slice(last_idx) });
160
+ if (last_idx < spec.length) {
161
+ parts.push(spec.slice(last_idx));
174
162
  }
175
- const result = has_token ? { parts, repl } : null;
163
+ const result = has_token ? parts : null;
176
164
  spec_cache.set(spec, result);
177
165
  return result;
178
166
  }
@@ -189,26 +177,16 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEF
189
177
  throw new TypeError('format: locale must be a string');
190
178
  if (typeof zone !== 'string')
191
179
  throw new TypeError('format: zone must be a string');
192
- const n_spec = getSpecChain(SPEC_ALIASES[spec] || spec);
193
- if (!n_spec)
180
+ const parts = getSpecChain(SPEC_ALIASES[spec] || spec);
181
+ if (!parts)
194
182
  return n_val.toISOString();
195
183
  const d = toZone(n_val, zone);
196
- const { parts, repl } = n_spec;
197
184
  let out = '';
198
185
  for (let i = 0; i < parts.length; i++) {
199
186
  const part = parts[i];
200
- if ('literal' in part) {
201
- out += part.literal;
202
- }
203
- else {
204
- out += part.token[1](d, locale, sow);
205
- }
187
+ out += typeof part === 'string' ? part : part(d, locale, sow);
206
188
  }
207
- let result = out;
208
- for (let i = 0; i < repl.length; i++) {
209
- result = result.replace(repl[i][0], repl[i][1]);
210
- }
211
- return result;
189
+ return out;
212
190
  }
213
191
  format.getLocale = function () {
214
192
  return DEFAULT_LOCALE;
package/cjs/date/is.js CHANGED
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isDate = isDate;
4
4
  exports.default = isDate;
5
5
  function isDate(val) {
6
- return val instanceof Date && !isNaN(val);
6
+ return val instanceof Date && val.getTime() === val.getTime();
7
7
  }
@@ -8,47 +8,49 @@ exports.isDateFormat = isDateFormat;
8
8
  exports.default = isDateFormat;
9
9
  const LRU_1 = __importDefault(require("../caching/LRU"));
10
10
  const SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
11
+ const CTX = { year: 0, month: 0, day: 0, hour: -1, is12: 0 };
11
12
  const TOKENS = [
12
- ['YYYY', /\d{4}/.source, (raw, context) => {
13
- context.year = raw | 0;
14
- return context.year > 0;
13
+ ['YYYY', /\d{4}/.source, raw => {
14
+ CTX.year = +raw;
15
+ return CTX.year > 0;
15
16
  }],
16
- ['MM', /(?:0[1-9]|1[0-2])/.source, (raw, context) => {
17
- context.month = raw | 0;
18
- return context.month >= 1 && context.month <= 12;
17
+ ['MM', /(?:0[1-9]|1[0-2])/.source, raw => {
18
+ CTX.month = +raw;
19
+ return CTX.month >= 1 && CTX.month <= 12;
19
20
  }],
20
- ['DD', /(?:0[1-9]|[12][0-9]|3[01])/.source, (raw, context) => {
21
- context.day = raw | 0;
22
- return context.day >= 1 && context.day <= 31;
21
+ ['DD', /(?:0[1-9]|[12][0-9]|3[01])/.source, raw => {
22
+ CTX.day = +raw;
23
+ return CTX.day >= 1 && CTX.day <= 31;
23
24
  }],
24
- ['HH', /(?:[01][0-9]|2[0-3])/.source, (raw, context) => {
25
- context.hour = raw | 0;
26
- return context.hour >= 0 && context.hour <= 23;
25
+ ['HH', /(?:[01][0-9]|2[0-3])/.source, raw => {
26
+ CTX.hour = +raw;
27
+ return CTX.hour >= 0 && CTX.hour <= 23;
27
28
  }],
28
29
  ['mm', /[0-5][0-9]/.source, () => true],
29
30
  ['ss', /[0-5][0-9]/.source, () => true],
30
31
  ['SSS', /\d{3}/.source, () => true],
31
32
  ['Q', /[1-4]/.source, () => true],
32
- ['A', /(?:AM|PM)/.source, (raw, context) => {
33
- context.is12 = 1;
33
+ ['A', /(?:AM|PM)/.source, raw => {
34
+ CTX.is12 = 1;
34
35
  return raw === 'AM' || raw === 'PM';
35
36
  }],
36
- ['a', /(?:am|pm)/.source, (raw, context) => {
37
- context.is12 = 1;
37
+ ['a', /(?:am|pm)/.source, raw => {
38
+ CTX.is12 = 1;
38
39
  return raw === 'am' || raw === 'pm';
39
40
  }],
40
41
  ['Z', /Z|[+-](?:0[0-9]|1[0-4]):[0-5][0-9]/.source, raw => {
41
42
  if (raw === 'Z')
42
43
  return true;
43
- let hour = (raw[1] + raw[2]) | 0;
44
- if (raw[0] === '-')
44
+ let hour = +(raw[1] + raw[2]);
45
+ if (raw.charCodeAt(0) === 45)
45
46
  hour = -hour;
46
- const minutes = (raw[4] + raw[5]) | 0;
47
+ const minutes = +(raw[4] + raw[5]);
47
48
  if (hour === 14 || hour === -12)
48
49
  return minutes === 0;
49
- return hour >= -11 && hour < 14 && [0, 15, 30, 45].indexOf(minutes) >= 0;
50
+ return hour >= -11 && hour < 14 && (minutes === 0 || minutes === 15 || minutes === 30 || minutes === 45);
50
51
  }],
51
52
  ];
53
+ TOKENS.sort((a, b) => b[0].length - a[0].length);
52
54
  const SPEC_ALIASES = {
53
55
  ISO: 'YYYY-MM-DDTHH:mm:ss{.SSS}Z',
54
56
  };
@@ -85,14 +87,18 @@ function compileSpec(spec, is_chunk = false) {
85
87
  cursor = end_idx + 1;
86
88
  }
87
89
  else {
88
- const token_idx = TOKENS.findIndex(([token_key]) => spec.startsWith(token_key, cursor));
89
- if (token_idx >= 0) {
90
- const [token_key, token_rgx] = TOKENS[token_idx];
91
- pat += '(' + token_rgx + ')';
92
- tokens.push(token_idx);
93
- cursor += token_key.length;
90
+ let matched = false;
91
+ for (let i = 0; i < TOKENS.length; i++) {
92
+ const [token_key, token_rgx] = TOKENS[i];
93
+ if (spec.startsWith(token_key, cursor)) {
94
+ pat += '(' + token_rgx + ')';
95
+ tokens.push(i);
96
+ cursor += token_key.length;
97
+ matched = true;
98
+ break;
99
+ }
94
100
  }
95
- else {
101
+ if (!matched) {
96
102
  pat += spec[cursor].replace(SPECIAL_CHARS, '\\$&');
97
103
  cursor++;
98
104
  }
@@ -107,23 +113,26 @@ function isDateFormat(input, spec) {
107
113
  return false;
108
114
  if (typeof spec !== 'string')
109
115
  throw new TypeError('isDateFormat: spec must be a string');
110
- const { tokens, rgx } = compileSpec(SPEC_ALIASES[spec] || spec);
111
- if (!tokens.length)
116
+ const compiled = compileSpec(SPEC_ALIASES[spec] || spec);
117
+ if (!compiled.tokens.length)
112
118
  return false;
113
- const patMatch = rgx.exec(input);
119
+ const patMatch = compiled.rgx.exec(input);
114
120
  if (!patMatch)
115
121
  return false;
116
- const matches = patMatch.slice(1);
117
- const context = {};
118
- for (let i = 0; i < matches.length; i++) {
119
- const match = matches[i];
120
- if (match !== undefined && !TOKENS[tokens[i]][2](match, context))
122
+ CTX.year = 0;
123
+ CTX.month = 0;
124
+ CTX.day = 0;
125
+ CTX.hour = -1;
126
+ CTX.is12 = 0;
127
+ const { tokens } = compiled;
128
+ for (let i = 0; i < tokens.length; i++) {
129
+ const match = patMatch[i + 1];
130
+ if (match !== undefined && !TOKENS[tokens[i]][2](match))
121
131
  return false;
122
132
  }
123
- const { is12, day, month, year } = context;
124
- if (day && month && !isValidDay(year || 2024, month, day))
133
+ if (CTX.day > 0 && CTX.month > 0 && !isValidDay(CTX.year || 2024, CTX.month, CTX.day))
125
134
  return false;
126
- if (is12 && 'hour' in context && context.hour > 11)
135
+ if (CTX.is12 === 1 && CTX.hour > 11)
127
136
  return false;
128
137
  return true;
129
138
  }
@@ -114,7 +114,7 @@ function toObject(form, config) {
114
114
  }
115
115
  if (nNumber && value.charCodeAt(0) !== 48) {
116
116
  const nVal = +value;
117
- if (!isNaN(nVal)) {
117
+ if (nVal === nVal) {
118
118
  assign(acc, key, nVal, single);
119
119
  continue;
120
120
  }
package/cjs/hash/hexId.js CHANGED
@@ -11,7 +11,7 @@ const POOL_SIZE = 16 * 1024;
11
11
  const pool = new Uint8Array(POOL_SIZE);
12
12
  let poolIdx = POOL_SIZE;
13
13
  function hexId(size) {
14
- if (typeof size !== 'number' || size <= 0)
14
+ if (typeof size !== 'number' || size <= 0 || (size | 0) !== size)
15
15
  return '';
16
16
  if (size > POOL_SIZE) {
17
17
  const buf = new Uint8Array(size);
@@ -6,7 +6,7 @@ const ROUND_EPSILON = 1 + Number.EPSILON;
6
6
  function round(val, precision = 0) {
7
7
  if (!Number.isFinite(val))
8
8
  throw new TypeError('Value should be numeric');
9
- if (!Number.isInteger(precision) || precision <= 0)
9
+ if (typeof precision !== 'number' || precision <= 0 || (precision | 0) !== precision)
10
10
  return Math.round(val * ROUND_EPSILON);
11
11
  const exp = Math.pow(10, precision);
12
12
  return Math.round((val * exp) * ROUND_EPSILON) / exp;
@@ -1,12 +1,19 @@
1
- import { isDate } from './is';
2
1
  function convertToDate(val) {
3
- if (isDate(val)) {
4
- return val;
2
+ switch (typeof val) {
3
+ case 'number': {
4
+ if (val !== val)
5
+ return null;
6
+ const d = new Date(val);
7
+ return d.getTime() === d.getTime() ? d : null;
8
+ }
9
+ case 'object':
10
+ return val instanceof Date && val.getTime() === val.getTime() ? val : null;
11
+ case 'string': {
12
+ const d = new Date(val);
13
+ return d.getTime() === d.getTime() ? d : null;
14
+ }
15
+ default:
16
+ return null;
5
17
  }
6
- else if (typeof val === 'string') {
7
- const date = new Date(val);
8
- return Number.isNaN(date.getTime()) ? null : date;
9
- }
10
- return null;
11
18
  }
12
19
  export { convertToDate, convertToDate as default };
@@ -1,5 +1,4 @@
1
1
  import { convertToDate } from './convertToDate';
2
- import { MONTHS, MONTHS_LEAP } from './isFormat';
3
2
  import LRU from '../caching/LRU';
4
3
  const WEEK_STARTS = {
5
4
  mon: 'mon',
@@ -9,7 +8,6 @@ const WEEK_STARTS = {
9
8
  let DEFAULT_LOCALE = 'en-US';
10
9
  let DEFAULT_TZ = Intl?.DateTimeFormat?.().resolvedOptions?.().timeZone || 'UTC';
11
10
  let DEFAULT_SOW = 'mon';
12
- const ESCAPE_RGX = /\[([^\]]*)]/g;
13
11
  const intl_formatters = new LRU({ max_size: 100 });
14
12
  const spec_cache = new LRU({ max_size: 100 });
15
13
  const zone_offset_cache = new LRU({ max_size: 100 });
@@ -35,15 +33,8 @@ function WeekNr(d, sow) {
35
33
  }
36
34
  }
37
35
  function toZone(d, zone) {
38
- const year = d.getUTCFullYear();
39
- const month = d.getUTCMonth();
40
- const day = d.getUTCDate();
41
36
  const time = d.getTime();
42
- const daysInMonths = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0) ? MONTHS_LEAP : MONTHS;
43
- let doy = day;
44
- for (let i = 0; i <= month; i++)
45
- doy += daysInMonths[i];
46
- const ckey = zone + ':' + year + ':' + doy;
37
+ const ckey = zone + ':' + Math.floor(time / 900000);
47
38
  const cached = zone_offset_cache.get(ckey);
48
39
  if (cached !== undefined)
49
40
  return new Date(time + cached);
@@ -73,54 +64,54 @@ function runIntl(loc, token, props, val) {
73
64
  return formatter.format(val);
74
65
  }
75
66
  const Tokens = [
76
- ['YYYY', d => d.getFullYear()],
77
- ['Q', d => ((d.getMonth() + 3) / 3) | 0],
67
+ ['YYYY', d => d.getFullYear().toString()],
68
+ ['Q', d => (((d.getMonth() + 3) / 3) | 0).toString()],
78
69
  ['MMMM', (d, loc) => runIntl(loc, 'MMMM', { month: 'long' }, d)],
79
70
  ['MMM', (d, loc) => runIntl(loc, 'MMM', { month: 'short' }, d)],
80
71
  ['MM', d => {
81
72
  const val = d.getMonth() + 1;
82
73
  return (val < 10 ? '0' : '') + val;
83
74
  }],
84
- ['M', d => d.getMonth() + 1],
75
+ ['M', d => (d.getMonth() + 1).toString()],
85
76
  ['WW', (d, loc, sow) => {
86
77
  const val = WeekNr(d, sow);
87
78
  return (val < 10 ? '0' : '') + val;
88
79
  }],
89
- ['W', (d, loc, sow) => WeekNr(d, sow)],
80
+ ['W', (d, loc, sow) => WeekNr(d, sow).toString()],
90
81
  ['DD', d => {
91
82
  const val = d.getDate();
92
83
  return (val < 10 ? '0' : '') + val;
93
84
  }],
94
- ['D', d => d.getDate()],
85
+ ['D', d => d.getDate().toString()],
95
86
  ['dddd', (d, loc) => runIntl(loc, 'dddd', { weekday: 'long' }, d)],
96
87
  ['ddd', (d, loc) => runIntl(loc, 'ddd', { weekday: 'short' }, d)],
97
88
  ['HH', d => {
98
89
  const val = d.getHours();
99
90
  return (val < 10 ? '0' : '') + val;
100
91
  }],
101
- ['H', d => d.getHours()],
92
+ ['H', d => d.getHours().toString()],
102
93
  ['hh', d => {
103
94
  const val = ((d.getHours() + 11) % 12) + 1;
104
95
  return (val < 10 ? '0' : '') + val;
105
96
  }],
106
- ['h', d => ((d.getHours() + 11) % 12) + 1],
97
+ ['h', d => (((d.getHours() + 11) % 12) + 1).toString()],
107
98
  ['mm', d => {
108
99
  const val = d.getMinutes();
109
100
  return (val < 10 ? '0' : '') + val;
110
101
  }],
111
- ['m', d => d.getMinutes()],
102
+ ['m', d => d.getMinutes().toString()],
112
103
  ['ss', d => {
113
104
  const val = d.getSeconds();
114
105
  return (val < 10 ? '0' : '') + val;
115
106
  }],
116
- ['s', d => d.getSeconds()],
107
+ ['s', d => d.getSeconds().toString()],
117
108
  ['SSS', d => {
118
109
  const val = d.getMilliseconds();
119
110
  return val < 10
120
111
  ? '00' + val
121
112
  : val < 100
122
113
  ? '0' + val
123
- : val;
114
+ : val.toString();
124
115
  }],
125
116
  ['A', d => d.getHours() < 12 ? 'AM' : 'PM'],
126
117
  ['a', d => d.getHours() < 12 ? 'am' : 'pm'],
@@ -134,38 +125,35 @@ const Tokens = [
134
125
  const token_map = {};
135
126
  for (const t of Tokens)
136
127
  token_map[t[0]] = t;
137
- const TOKENS_RGX = new RegExp(Tokens.map(([tok]) => tok).join('|'), 'g');
128
+ const PARSE_RGX = new RegExp('\\[[^\\]]*\\]|' + Tokens.map(([tok]) => tok).join('|'), 'g');
138
129
  function getSpecChain(spec) {
139
130
  const cached = spec_cache.get(spec);
140
131
  if (cached !== undefined)
141
132
  return cached;
142
- let base = spec;
143
- const repl = [];
144
- let repl_len = 0;
145
- if (base.indexOf('[') >= 0) {
146
- base = base.replace(ESCAPE_RGX, (_, inner) => {
147
- const escape_token = '$' + repl_len++ + '$';
148
- repl.push([escape_token, inner]);
149
- return escape_token;
150
- });
151
- }
152
- TOKENS_RGX.lastIndex = 0;
153
133
  const parts = [];
134
+ PARSE_RGX.lastIndex = 0;
154
135
  let last_idx = 0;
155
136
  let has_token = false;
156
137
  let m;
157
- while (m = TOKENS_RGX.exec(base)) {
158
- if (m.index > last_idx) {
159
- parts.push({ literal: base.slice(last_idx, m.index) });
138
+ while (m = PARSE_RGX.exec(spec)) {
139
+ const match = m[0];
140
+ const match_start = m.index;
141
+ if (match_start > last_idx) {
142
+ parts.push(spec.slice(last_idx, match_start));
143
+ }
144
+ if (match.charCodeAt(0) === 91) {
145
+ parts.push(match.slice(1, -1));
160
146
  }
161
- parts.push({ token: token_map[m[0]] });
162
- has_token = true;
163
- last_idx = m.index + m[0].length;
147
+ else {
148
+ parts.push(token_map[match][1]);
149
+ has_token = true;
150
+ }
151
+ last_idx = match_start + match.length;
164
152
  }
165
- if (last_idx < base.length) {
166
- parts.push({ literal: base.slice(last_idx) });
153
+ if (last_idx < spec.length) {
154
+ parts.push(spec.slice(last_idx));
167
155
  }
168
- const result = has_token ? { parts, repl } : null;
156
+ const result = has_token ? parts : null;
169
157
  spec_cache.set(spec, result);
170
158
  return result;
171
159
  }
@@ -182,26 +170,16 @@ function format(val, spec, locale = DEFAULT_LOCALE, zone = DEFAULT_TZ, sow = DEF
182
170
  throw new TypeError('format: locale must be a string');
183
171
  if (typeof zone !== 'string')
184
172
  throw new TypeError('format: zone must be a string');
185
- const n_spec = getSpecChain(SPEC_ALIASES[spec] || spec);
186
- if (!n_spec)
173
+ const parts = getSpecChain(SPEC_ALIASES[spec] || spec);
174
+ if (!parts)
187
175
  return n_val.toISOString();
188
176
  const d = toZone(n_val, zone);
189
- const { parts, repl } = n_spec;
190
177
  let out = '';
191
178
  for (let i = 0; i < parts.length; i++) {
192
179
  const part = parts[i];
193
- if ('literal' in part) {
194
- out += part.literal;
195
- }
196
- else {
197
- out += part.token[1](d, locale, sow);
198
- }
180
+ out += typeof part === 'string' ? part : part(d, locale, sow);
199
181
  }
200
- let result = out;
201
- for (let i = 0; i < repl.length; i++) {
202
- result = result.replace(repl[i][0], repl[i][1]);
203
- }
204
- return result;
182
+ return out;
205
183
  }
206
184
  format.getLocale = function () {
207
185
  return DEFAULT_LOCALE;
package/esm/date/is.js CHANGED
@@ -1,4 +1,4 @@
1
1
  function isDate(val) {
2
- return val instanceof Date && !isNaN(val);
2
+ return val instanceof Date && val.getTime() === val.getTime();
3
3
  }
4
4
  export { isDate, isDate as default };
@@ -1,46 +1,48 @@
1
1
  import LRU from '../caching/LRU';
2
2
  const SPECIAL_CHARS = /[.*+?^${}()|[\]\\]/g;
3
+ const CTX = { year: 0, month: 0, day: 0, hour: -1, is12: 0 };
3
4
  const TOKENS = [
4
- ['YYYY', /\d{4}/.source, (raw, context) => {
5
- context.year = raw | 0;
6
- return context.year > 0;
5
+ ['YYYY', /\d{4}/.source, raw => {
6
+ CTX.year = +raw;
7
+ return CTX.year > 0;
7
8
  }],
8
- ['MM', /(?:0[1-9]|1[0-2])/.source, (raw, context) => {
9
- context.month = raw | 0;
10
- return context.month >= 1 && context.month <= 12;
9
+ ['MM', /(?:0[1-9]|1[0-2])/.source, raw => {
10
+ CTX.month = +raw;
11
+ return CTX.month >= 1 && CTX.month <= 12;
11
12
  }],
12
- ['DD', /(?:0[1-9]|[12][0-9]|3[01])/.source, (raw, context) => {
13
- context.day = raw | 0;
14
- return context.day >= 1 && context.day <= 31;
13
+ ['DD', /(?:0[1-9]|[12][0-9]|3[01])/.source, raw => {
14
+ CTX.day = +raw;
15
+ return CTX.day >= 1 && CTX.day <= 31;
15
16
  }],
16
- ['HH', /(?:[01][0-9]|2[0-3])/.source, (raw, context) => {
17
- context.hour = raw | 0;
18
- return context.hour >= 0 && context.hour <= 23;
17
+ ['HH', /(?:[01][0-9]|2[0-3])/.source, raw => {
18
+ CTX.hour = +raw;
19
+ return CTX.hour >= 0 && CTX.hour <= 23;
19
20
  }],
20
21
  ['mm', /[0-5][0-9]/.source, () => true],
21
22
  ['ss', /[0-5][0-9]/.source, () => true],
22
23
  ['SSS', /\d{3}/.source, () => true],
23
24
  ['Q', /[1-4]/.source, () => true],
24
- ['A', /(?:AM|PM)/.source, (raw, context) => {
25
- context.is12 = 1;
25
+ ['A', /(?:AM|PM)/.source, raw => {
26
+ CTX.is12 = 1;
26
27
  return raw === 'AM' || raw === 'PM';
27
28
  }],
28
- ['a', /(?:am|pm)/.source, (raw, context) => {
29
- context.is12 = 1;
29
+ ['a', /(?:am|pm)/.source, raw => {
30
+ CTX.is12 = 1;
30
31
  return raw === 'am' || raw === 'pm';
31
32
  }],
32
33
  ['Z', /Z|[+-](?:0[0-9]|1[0-4]):[0-5][0-9]/.source, raw => {
33
34
  if (raw === 'Z')
34
35
  return true;
35
- let hour = (raw[1] + raw[2]) | 0;
36
- if (raw[0] === '-')
36
+ let hour = +(raw[1] + raw[2]);
37
+ if (raw.charCodeAt(0) === 45)
37
38
  hour = -hour;
38
- const minutes = (raw[4] + raw[5]) | 0;
39
+ const minutes = +(raw[4] + raw[5]);
39
40
  if (hour === 14 || hour === -12)
40
41
  return minutes === 0;
41
- return hour >= -11 && hour < 14 && [0, 15, 30, 45].indexOf(minutes) >= 0;
42
+ return hour >= -11 && hour < 14 && (minutes === 0 || minutes === 15 || minutes === 30 || minutes === 45);
42
43
  }],
43
44
  ];
45
+ TOKENS.sort((a, b) => b[0].length - a[0].length);
44
46
  const SPEC_ALIASES = {
45
47
  ISO: 'YYYY-MM-DDTHH:mm:ss{.SSS}Z',
46
48
  };
@@ -77,14 +79,18 @@ function compileSpec(spec, is_chunk = false) {
77
79
  cursor = end_idx + 1;
78
80
  }
79
81
  else {
80
- const token_idx = TOKENS.findIndex(([token_key]) => spec.startsWith(token_key, cursor));
81
- if (token_idx >= 0) {
82
- const [token_key, token_rgx] = TOKENS[token_idx];
83
- pat += '(' + token_rgx + ')';
84
- tokens.push(token_idx);
85
- cursor += token_key.length;
82
+ let matched = false;
83
+ for (let i = 0; i < TOKENS.length; i++) {
84
+ const [token_key, token_rgx] = TOKENS[i];
85
+ if (spec.startsWith(token_key, cursor)) {
86
+ pat += '(' + token_rgx + ')';
87
+ tokens.push(i);
88
+ cursor += token_key.length;
89
+ matched = true;
90
+ break;
91
+ }
86
92
  }
87
- else {
93
+ if (!matched) {
88
94
  pat += spec[cursor].replace(SPECIAL_CHARS, '\\$&');
89
95
  cursor++;
90
96
  }
@@ -99,23 +105,26 @@ function isDateFormat(input, spec) {
99
105
  return false;
100
106
  if (typeof spec !== 'string')
101
107
  throw new TypeError('isDateFormat: spec must be a string');
102
- const { tokens, rgx } = compileSpec(SPEC_ALIASES[spec] || spec);
103
- if (!tokens.length)
108
+ const compiled = compileSpec(SPEC_ALIASES[spec] || spec);
109
+ if (!compiled.tokens.length)
104
110
  return false;
105
- const patMatch = rgx.exec(input);
111
+ const patMatch = compiled.rgx.exec(input);
106
112
  if (!patMatch)
107
113
  return false;
108
- const matches = patMatch.slice(1);
109
- const context = {};
110
- for (let i = 0; i < matches.length; i++) {
111
- const match = matches[i];
112
- if (match !== undefined && !TOKENS[tokens[i]][2](match, context))
114
+ CTX.year = 0;
115
+ CTX.month = 0;
116
+ CTX.day = 0;
117
+ CTX.hour = -1;
118
+ CTX.is12 = 0;
119
+ const { tokens } = compiled;
120
+ for (let i = 0; i < tokens.length; i++) {
121
+ const match = patMatch[i + 1];
122
+ if (match !== undefined && !TOKENS[tokens[i]][2](match))
113
123
  return false;
114
124
  }
115
- const { is12, day, month, year } = context;
116
- if (day && month && !isValidDay(year || 2024, month, day))
125
+ if (CTX.day > 0 && CTX.month > 0 && !isValidDay(CTX.year || 2024, CTX.month, CTX.day))
117
126
  return false;
118
- if (is12 && 'hour' in context && context.hour > 11)
127
+ if (CTX.is12 === 1 && CTX.hour > 11)
119
128
  return false;
120
129
  return true;
121
130
  }
@@ -110,7 +110,7 @@ function toObject(form, config) {
110
110
  }
111
111
  if (nNumber && value.charCodeAt(0) !== 48) {
112
112
  const nVal = +value;
113
- if (!isNaN(nVal)) {
113
+ if (nVal === nVal) {
114
114
  assign(acc, key, nVal, single);
115
115
  continue;
116
116
  }
package/esm/hash/hexId.js CHANGED
@@ -7,7 +7,7 @@ const POOL_SIZE = 16 * 1024;
7
7
  const pool = new Uint8Array(POOL_SIZE);
8
8
  let poolIdx = POOL_SIZE;
9
9
  function hexId(size) {
10
- if (typeof size !== 'number' || size <= 0)
10
+ if (typeof size !== 'number' || size <= 0 || (size | 0) !== size)
11
11
  return '';
12
12
  if (size > POOL_SIZE) {
13
13
  const buf = new Uint8Array(size);
@@ -2,7 +2,7 @@ const ROUND_EPSILON = 1 + Number.EPSILON;
2
2
  function round(val, precision = 0) {
3
3
  if (!Number.isFinite(val))
4
4
  throw new TypeError('Value should be numeric');
5
- if (!Number.isInteger(precision) || precision <= 0)
5
+ if (typeof precision !== 'number' || precision <= 0 || (precision | 0) !== precision)
6
6
  return Math.round(val * ROUND_EPSILON);
7
7
  const exp = Math.pow(10, precision);
8
8
  return Math.round((val * exp) * ROUND_EPSILON) / exp;
package/index.d.ts CHANGED
@@ -163,10 +163,6 @@ declare module "boolean/is" {
163
163
  declare module "boolean/index" {
164
164
  export { isBoolean } from "boolean/is";
165
165
  }
166
- declare module "date/is" {
167
- function isDate(val: unknown): val is Date;
168
- export { isDate, isDate as default };
169
- }
170
166
  declare module "date/convertToDate" {
171
167
  function convertToDate(val: Date | string | number): Date | null;
172
168
  export { convertToDate, convertToDate as default };
@@ -211,12 +207,6 @@ declare module "caching/LRU" {
211
207
  }
212
208
  export { LRUCache, LRUCache as default };
213
209
  }
214
- declare module "date/isFormat" {
215
- export const MONTHS_LEAP: number[];
216
- export const MONTHS: number[];
217
- function isDateFormat(input: unknown, spec: string): input is string;
218
- export { isDateFormat, isDateFormat as default };
219
- }
220
210
  declare module "date/format" {
221
211
  const WEEK_STARTS: {
222
212
  readonly mon: "mon";
@@ -266,6 +256,10 @@ declare module "date/startOfUTC" {
266
256
  function startOfUTC(val: Date | string, key?: StartOfUTCKey): Date;
267
257
  export { startOfUTC, startOfUTC as default };
268
258
  }
259
+ declare module "date/is" {
260
+ function isDate(val: unknown): val is Date;
261
+ export { isDate, isDate as default };
262
+ }
269
263
  declare module "date/toUnix" {
270
264
  function toUnix(val: Date): number;
271
265
  export { toUnix, toUnix as default };
@@ -274,6 +268,12 @@ declare module "date/toUTC" {
274
268
  function toUTC(val: Date): Date;
275
269
  export { toUTC, toUTC as default };
276
270
  }
271
+ declare module "date/isFormat" {
272
+ export const MONTHS_LEAP: number[];
273
+ export const MONTHS: number[];
274
+ function isDateFormat(input: unknown, spec: string): input is string;
275
+ export { isDateFormat, isDateFormat as default };
276
+ }
277
277
  declare module "date/index" {
278
278
  export { addUTC } from "date/addUTC";
279
279
  export { convertToDate } from "date/convertToDate";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valkyriestudios/utils",
3
- "version": "12.48.0",
3
+ "version": "12.49.0",
4
4
  "description": "A collection of single-function utilities for common tasks",
5
5
  "author": {
6
6
  "name": "Peter Vermeulen",
@@ -609,12 +609,12 @@
609
609
  }
610
610
  },
611
611
  "devDependencies": {
612
- "@types/node": "^22.19.3",
613
- "@vitest/coverage-v8": "^4.0.16",
612
+ "@types/node": "^22.19.11",
613
+ "@vitest/coverage-v8": "^4.0.18",
614
614
  "esbuild-register": "^3.6.0",
615
615
  "eslint": "^9.39.2",
616
616
  "typescript": "^5.9.3",
617
617
  "typescript-eslint": "^8.51.0",
618
- "vitest": "^4.0.16"
618
+ "vitest": "^4.0.18"
619
619
  }
620
620
  }