@jarenjs/core 0.9.2 → 0.34.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.
Files changed (121) hide show
  1. package/ARCHITECTURE.md +229 -26
  2. package/README.md +64 -10
  3. package/dist/types/array.d.ts +13 -0
  4. package/dist/types/cache.d.ts +166 -0
  5. package/dist/types/chunk.d.ts +102 -0
  6. package/dist/types/color.d.ts +64 -0
  7. package/dist/types/convert/convert.d.ts +33 -0
  8. package/dist/types/convert/currency.d.ts +47 -0
  9. package/dist/types/convert/index.d.ts +9 -0
  10. package/dist/types/convert/registry.d.ts +55 -0
  11. package/dist/types/dates/civil.d.ts +146 -0
  12. package/dist/types/dates/duration.d.ts +83 -0
  13. package/dist/types/dates/format.d.ts +59 -0
  14. package/dist/types/dates/index.d.ts +4 -0
  15. package/dist/types/{dates.d.ts → dates/rfc3339.d.ts} +57 -3
  16. package/dist/types/errors.d.ts +77 -0
  17. package/dist/types/finance/amortization.d.ts +51 -0
  18. package/dist/types/finance/bond.d.ts +45 -0
  19. package/dist/types/finance/cashflow.d.ts +44 -0
  20. package/dist/types/finance/depreciation.d.ts +26 -0
  21. package/dist/types/finance/index.d.ts +16 -0
  22. package/dist/types/finance/indicators.d.ts +76 -0
  23. package/dist/types/finance/interest.d.ts +44 -0
  24. package/dist/types/finance/returns.d.ts +43 -0
  25. package/dist/types/finance/tvm.d.ts +50 -0
  26. package/dist/types/geo/angle.d.ts +4 -0
  27. package/dist/types/geo/bbox.d.ts +32 -0
  28. package/dist/types/geo/distance.d.ts +67 -0
  29. package/dist/types/geo/geohash.d.ts +63 -0
  30. package/dist/types/geo/geojson.d.ts +108 -0
  31. package/dist/types/geo/index-tree.d.ts +52 -0
  32. package/dist/types/geo/index.d.ts +11 -0
  33. package/dist/types/geo/mercator.d.ts +57 -0
  34. package/dist/types/geo/predicates.d.ts +44 -0
  35. package/dist/types/geo/ring.d.ts +80 -0
  36. package/dist/types/geo/simplify.d.ts +31 -0
  37. package/dist/types/geo/valid.d.ts +24 -0
  38. package/dist/types/geo/wkt.d.ts +16 -0
  39. package/dist/types/math/float64.d.ts +126 -3
  40. package/dist/types/math/format.d.ts +58 -0
  41. package/dist/types/math/index.d.ts +5 -0
  42. package/dist/types/math/mat4.d.ts +60 -0
  43. package/dist/types/math/project.d.ts +54 -0
  44. package/dist/types/math/solve.d.ts +67 -0
  45. package/dist/types/math/word.d.ts +82 -0
  46. package/dist/types/message.d.ts +52 -0
  47. package/dist/types/object.d.ts +151 -0
  48. package/dist/types/scan.d.ts +30 -0
  49. package/dist/types/schema.d.ts +24 -0
  50. package/dist/types/string.d.ts +96 -0
  51. package/dist/types/text/base64.d.ts +6 -4
  52. package/dist/types/text/email.d.ts +0 -1
  53. package/dist/types/text/host.d.ts +39 -8
  54. package/dist/types/text/i18n.d.ts +13 -1
  55. package/dist/types/text/index.d.ts +1 -0
  56. package/dist/types/text/misc.d.ts +15 -1
  57. package/dist/types/text/punycode.d.ts +45 -85
  58. package/docs/CONVERT.md +45 -0
  59. package/docs/DATES.md +78 -0
  60. package/docs/FINANCE.md +59 -0
  61. package/docs/GEO.md +114 -0
  62. package/docs/MATH.md +75 -0
  63. package/package.json +57 -4
  64. package/src/array.js +16 -0
  65. package/src/cache.js +206 -0
  66. package/src/chunk.js +159 -0
  67. package/src/color.js +125 -0
  68. package/src/convert/convert.js +59 -0
  69. package/src/convert/currency.js +74 -0
  70. package/src/convert/index.js +11 -0
  71. package/src/convert/registry.js +213 -0
  72. package/src/dates/civil.js +360 -0
  73. package/src/dates/duration.js +225 -0
  74. package/src/dates/format.js +238 -0
  75. package/src/dates/index.js +30 -0
  76. package/src/dates/rfc3339.js +621 -0
  77. package/src/errors.js +102 -0
  78. package/src/finance/amortization.js +50 -0
  79. package/src/finance/bond.js +100 -0
  80. package/src/finance/cashflow.js +122 -0
  81. package/src/finance/depreciation.js +54 -0
  82. package/src/finance/index.js +27 -0
  83. package/src/finance/indicators.js +207 -0
  84. package/src/finance/interest.js +63 -0
  85. package/src/finance/returns.js +90 -0
  86. package/src/finance/tvm.js +94 -0
  87. package/src/function.js +0 -2
  88. package/src/geo/angle.js +12 -0
  89. package/src/geo/bbox.js +80 -0
  90. package/src/geo/distance.js +155 -0
  91. package/src/geo/geohash.js +224 -0
  92. package/src/geo/geojson.js +361 -0
  93. package/src/geo/index-tree.js +294 -0
  94. package/src/geo/index.js +52 -0
  95. package/src/geo/mercator.js +124 -0
  96. package/src/geo/predicates.js +313 -0
  97. package/src/geo/ring.js +182 -0
  98. package/src/geo/simplify.js +130 -0
  99. package/src/geo/valid.js +142 -0
  100. package/src/geo/wkt.js +262 -0
  101. package/src/math/float64.js +228 -11
  102. package/src/math/format.js +157 -0
  103. package/src/math/index.js +5 -0
  104. package/src/math/mat4.js +131 -0
  105. package/src/math/project.js +49 -0
  106. package/src/math/solve.js +112 -0
  107. package/src/math/word.js +177 -0
  108. package/src/message.js +119 -0
  109. package/src/object.js +329 -0
  110. package/src/scan.js +42 -0
  111. package/src/schema.js +37 -0
  112. package/src/string.js +189 -0
  113. package/src/text/base64.js +15 -52
  114. package/src/text/email.js +0 -5
  115. package/src/text/host.js +515 -140
  116. package/src/text/i18n.js +13 -6
  117. package/src/text/identifiers.js +1 -1
  118. package/src/text/index.js +1 -0
  119. package/src/text/misc.js +81 -5
  120. package/src/text/punycode.js +235 -323
  121. package/src/dates.js +0 -371
package/src/dates.js DELETED
@@ -1,371 +0,0 @@
1
- //@ts-check
2
-
3
- import {
4
- isStringType,
5
- isObjectOfClass,
6
- } from './index.js';
7
-
8
- //#region Dates Constants
9
- export const CONST_TICKS_SECOND = 1000;
10
- export const CONST_TICKS_HOUR = CONST_TICKS_SECOND * 60 * 60;
11
- export const CONST_TICKS_DAY = CONST_TICKS_HOUR * 24;
12
-
13
- export const CONST_TIME_INSERTDATE = '1970-01-01T';
14
- export const CONST_DATE_APPENDTIME = 'T00:00:00Z';
15
-
16
- export const CONST_RFC3339_DAYS = Object.freeze(
17
- [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
18
- );
19
-
20
- // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
21
- export const CONST_RFC3339_REGEX_ISDATE = /^(\d\d\d\d)-([0-1]\d)-([0-3]\d)z?$/i;
22
-
23
- // full-date from http://tools.ietf.org/html/rfc3339#section-5.6
24
- export const CONST_RFC3339_REGEX_ISTIME = /^(\d\d):(\d\d):(\d\d)(\.\d{1,6})?(z|(([+-])(\d\d):(\d\d)))$/i;
25
-
26
- //#endregion
27
-
28
- //#region Dates Compare
29
- export function isDateType(data) {
30
- return isObjectOfClass(data, Date);
31
- }
32
-
33
- export function isDateishType(data) {
34
- return isDateType(data)
35
- || !Number.isNaN(Date.parse(data));
36
- }
37
-
38
- export function isLeapYear(year) {
39
- // https://tools.ietf.org/html/rfc3339#appendix-C
40
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
41
- }
42
-
43
- export function isDateOnlyInRange(year = 0, month = 0, day = 0) {
44
- return month >= 1
45
- && month <= 12
46
- && day >= 1
47
- && day <= (month === 2 && isLeapYear(year)
48
- ? 29
49
- : CONST_RFC3339_DAYS[month]);
50
- }
51
-
52
- export function isDateOnlyRFC3339(str) {
53
- if (!isStringType(str))
54
- return false;
55
-
56
- const r = str.match(CONST_RFC3339_REGEX_ISDATE);
57
- if (r == null)
58
- return false;
59
-
60
- const y = parseInt(r[1], 10) | 0;
61
- const m = parseInt(r[2], 10) | 0;
62
- const d = parseInt(r[3], 10) | 0;
63
- return isDateOnlyInRange(y, m, d);
64
- }
65
-
66
- export function isTimeOnlyInRange(hrs = 0, min = 0, sec = 0, tzh = 0, tzm = 0, tzSign = 1) {
67
- // Validate timezone offset range
68
- if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
69
- return false;
70
-
71
- // For leap seconds (sec === 60), we need to check if the corresponding UTC time
72
- // is 23:59:60. A leap second is only valid at the very end of a UTC day.
73
- if (sec === 60) {
74
- // Calculate what the UTC time would be
75
- // UTC = local - offset (if offset is positive, local is ahead of UTC)
76
- // So: UTC = local + (tzSign * tzh) hours + (tzSign * tzm) minutes
77
- let utcHrs = hrs - (tzSign * tzh);
78
- let utcMin = min - (tzSign * tzm);
79
-
80
- // Handle wrap-around
81
- while (utcMin < 0) {
82
- utcMin += 60;
83
- utcHrs -= 1;
84
- }
85
- while (utcMin >= 60) {
86
- utcMin -= 60;
87
- utcHrs += 1;
88
- }
89
- while (utcHrs < 0) {
90
- utcHrs += 24;
91
- }
92
- utcHrs = utcHrs % 24;
93
-
94
- // Leap second is only valid at 23:59:60 UTC
95
- return utcHrs === 23 && utcMin === 59 && sec === 60;
96
- }
97
-
98
- // Normal time validation (sec 0-59)
99
- return hrs >= 0 && hrs <= 23
100
- && min >= 0 && min <= 59
101
- && sec >= 0 && sec <= 59;
102
- }
103
-
104
- export function isTimeOnlyRFC3339(str) {
105
- if (!isStringType(str))
106
- return false;
107
-
108
- const r = str.match(CONST_RFC3339_REGEX_ISTIME);
109
- if (r == null)
110
- return false;
111
-
112
- const h = parseInt(r[1], 10) | 0;
113
- const m = parseInt(r[2], 10) | 0;
114
- const s = parseInt(r[3], 10) | 0;
115
- const th = parseInt(r[8], 10) | 0;
116
- const tm = parseInt(r[9], 10) | 0;
117
- // r[7] is the timezone sign (+ or -)
118
- // + means local time is ahead of UTC, so we subtract to get UTC
119
- // - means local time is behind UTC, so we add to get UTC
120
- const tzSign = r[7] === '-' ? -1 : 1;
121
- return isTimeOnlyInRange(h, m, s, th, tm, tzSign);
122
- }
123
-
124
- export function isDateTimeRFC3339(str) {
125
- // http://tools.ietf.org/html/rfc3339#section-5.6
126
- if (!isStringType(str)) return false;
127
- const dateTime = str.split(/t|\s/i);
128
- return dateTime.length === 2
129
- && isDateOnlyRFC3339(dateTime[0])
130
- && isTimeOnlyRFC3339(dateTime[1]);
131
- }
132
-
133
- //#endregion
134
-
135
- //#region Dates Getters
136
- export function getDateTypeOfDateOnlyRFC3339(str, def = undefined) {
137
- return isDateOnlyRFC3339(str)
138
- ? new Date(Date.parse(str))
139
- : def;
140
- }
141
-
142
- export function getDateTypeOfTimeOnlyRFC3339(str, def = undefined) {
143
- return isTimeOnlyRFC3339(str)
144
- ? new Date(Date.parse(CONST_TIME_INSERTDATE + str))
145
- : def;
146
- }
147
-
148
- export function getDateTypeOfDateTimeRFC3339(str, def = undefined) {
149
- return isDateTimeRFC3339(str)
150
- ? new Date(Date.parse(str))
151
- : def;
152
- }
153
- //#endregion
154
-
155
- //#region Duration Validation (RFC 3339)
156
- // Duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W
157
- // Examples: P1Y2M3DT4H5M6S, P1W, PT1H, P1Y
158
- // https://tools.ietf.org/html/rfc3339#appendix-A
159
-
160
- /**
161
- * Validates a duration string per RFC 3339.
162
- * Duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S or P[n]W
163
- *
164
- * @param {string} str - The duration string to validate
165
- * @returns {boolean} - True if the string is a valid duration
166
- * @example
167
- * isValidDuration('P1Y2M3DT4H5M6S'); // true (1 year, 2 months, 3 days, 4 hours, 5 minutes, 6 seconds)
168
- * isValidDuration('P1W'); // true (1 week)
169
- * isValidDuration('PT1H'); // true (1 hour)
170
- * isValidDuration('P1Y'); // true (1 year)
171
- * isValidDuration('P'); // false (empty duration)
172
- * isValidDuration('1Y'); // false (missing P)
173
- */
174
- export function isValidDuration(str) {
175
- if (!isStringType(str))
176
- return false;
177
-
178
- // Must start with P
179
- if (!str.startsWith('P'))
180
- return false;
181
-
182
- // Cannot be just "P"
183
- if (str.length < 2)
184
- return false;
185
-
186
- // Parse and validate components
187
- // Remove the 'P' prefix
188
- const rest = str.slice(1);
189
-
190
- // Check for week format: P[n]W (cannot be combined with other components)
191
- if (rest.endsWith('W')) {
192
- const weekPart = rest.slice(0, -1);
193
- return weekPart.length > 0 && /^\d+$/.test(weekPart);
194
- }
195
-
196
- // Split into date and time parts
197
- const tIndex = rest.indexOf('T');
198
- const datePart = tIndex >= 0 ? rest.slice(0, tIndex) : rest;
199
- const timePart = tIndex >= 0 ? rest.slice(tIndex + 1) : '';
200
-
201
- // Must have at least one component
202
- if (!datePart && !timePart)
203
- return false;
204
-
205
- // If there's a T, there must be at least one time component
206
- if (tIndex >= 0 && !timePart)
207
- return false;
208
-
209
- // Validate date part components (Y, M, D)
210
- if (datePart) {
211
- // Must match pattern: optional number+Y, optional number+M, optional number+D
212
- // in that order, at least one must be present
213
- const dateRegex = /^(\d+Y)?(\d+M)?(\d+D)?$/;
214
- if (!dateRegex.test(datePart))
215
- return false;
216
- // Must have at least one component
217
- if (!/\d+[YMD]/.test(datePart))
218
- return false;
219
- }
220
-
221
- // Validate time part components (H, M, S)
222
- if (timePart) {
223
- const timeRegex = /^(\d+H)?(\d+M)?(\d+(?:\.\d+)?S)?$/;
224
- if (!timeRegex.test(timePart))
225
- return false;
226
- // Must have at least one component
227
- if (!/\d+[HMS]/.test(timePart))
228
- return false;
229
- }
230
-
231
- return true;
232
- }
233
- //#endregion
234
-
235
- //#region ISO Date-Time and ISO Time (with optional timezone)
236
- // ISO 8601 date-time with optional timezone (like 2024-01-15T12:30:00)
237
- // ISO 8601 time with optional timezone (like 12:30:00)
238
-
239
- // Regex for iso-date-time: allows with or without timezone
240
- const CONST_ISO_REGEX_DATETIME = /^(\d{4})-([0-1]\d)-([0-3]\d)[T\s](\d{2}):(\d{2}):(\d{2})(\.\d{1,6})?(?:(Z)|([+-])(\d{2}):(\d{2}))?$/i;
241
-
242
- // Regex for iso-time: allows with or without timezone
243
- const CONST_ISO_REGEX_TIME = /^(\d{2}):(\d{2}):(\d{2})(\.\d{1,6})?(?:(Z)|([+-])(\d{2}):(\d{2}))?$/i;
244
-
245
- /**
246
- * Validates an ISO 8601 date-time string with optional timezone.
247
- * Unlike RFC 3339, the timezone is optional.
248
- *
249
- * @param {string} str - The date-time string to validate
250
- * @returns {boolean} - True if the string is a valid ISO date-time
251
- * @example
252
- * isValidISODateTime('2024-01-15T12:30:00Z'); // true
253
- * isValidISODateTime('2024-01-15T12:30:00+01:00'); // true
254
- * isValidISODateTime('2024-01-15T12:30:00'); // true (no timezone)
255
- * isValidISODateTime('2024-01-15 12:30:00'); // true (space separator)
256
- * isValidISODateTime('2024-13-15T12:30:00'); // false (invalid month)
257
- */
258
- export function isValidISODateTime(str) {
259
- if (!isStringType(str))
260
- return false;
261
-
262
- const r = str.match(CONST_ISO_REGEX_DATETIME);
263
- if (r == null)
264
- return false;
265
-
266
- const y = parseInt(r[1], 10) | 0;
267
- const m = parseInt(r[2], 10) | 0;
268
- const d = parseInt(r[3], 10) | 0;
269
- const h = parseInt(r[4], 10) | 0;
270
- const min = parseInt(r[5], 10) | 0;
271
- const s = parseInt(r[6], 10) | 0;
272
-
273
- // Validate date portion
274
- if (!isDateOnlyInRange(y, m, d))
275
- return false;
276
-
277
- // Validate time portion (no leap seconds for ISO date-time without explicit timezone)
278
- if (h < 0 || h > 23 || min < 0 || min > 59 || s < 0 || s > 59)
279
- return false;
280
-
281
- // Validate timezone if present
282
- if (r[9] != null) {
283
- const tzh = parseInt(r[10], 10) | 0;
284
- const tzm = parseInt(r[11], 10) | 0;
285
- if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
286
- return false;
287
- }
288
-
289
- return true;
290
- }
291
-
292
- /**
293
- * Validates an ISO 8601 time string with optional timezone.
294
- * Unlike RFC 3339, the timezone is optional.
295
- *
296
- * @param {string} str - The time string to validate
297
- * @returns {boolean} - True if the string is a valid ISO time
298
- * @example
299
- * isValidISOTime('12:30:00Z'); // true
300
- * isValidISOTime('12:30:00+01:00'); // true
301
- * isValidISOTime('12:30:00'); // true (no timezone)
302
- * isValidISOTime('25:00:00'); // false (invalid hour)
303
- */
304
- export function isValidISOTime(str) {
305
- if (!isStringType(str))
306
- return false;
307
-
308
- const r = str.match(CONST_ISO_REGEX_TIME);
309
- if (r == null)
310
- return false;
311
-
312
- const h = parseInt(r[1], 10) | 0;
313
- const m = parseInt(r[2], 10) | 0;
314
- const s = parseInt(r[3], 10) | 0;
315
-
316
- // Validate time portion (no leap seconds for ISO time without explicit timezone)
317
- if (h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59)
318
- return false;
319
-
320
- // Validate timezone if present
321
- if (r[7] != null) {
322
- const tzh = parseInt(r[8], 10) | 0;
323
- const tzm = parseInt(r[9], 10) | 0;
324
- if (tzh < 0 || tzh > 23 || tzm < 0 || tzm > 59)
325
- return false;
326
- }
327
-
328
- return true;
329
- }
330
-
331
- /**
332
- * Parses an ISO 8601 date-time string with optional timezone.
333
- * Returns a Date object if valid, otherwise undefined.
334
- *
335
- * @param {string} str - The date-time string to parse
336
- * @param {any} [def=undefined] - Default value to return if invalid
337
- * @returns {Date|undefined} - The parsed Date or default value
338
- */
339
- export function getDateTypeOfISODateTime(str, def = undefined) {
340
- if (!isValidISODateTime(str))
341
- return def;
342
-
343
- // If no timezone specified, treat as local time by appending Z
344
- // (ISO 8601 without timezone is local time, but for consistency we treat as UTC)
345
- if (!/[Z+-]\d{2}:\d{2}$/i.test(str) && !str.endsWith('Z')) {
346
- // Try parsing as-is (Date.parse handles both formats)
347
- const date = new Date(Date.parse(str.replace(' ', 'T')));
348
- return isNaN(date.getTime()) ? def : date;
349
- }
350
-
351
- return new Date(Date.parse(str));
352
- }
353
-
354
- /**
355
- * Parses an ISO 8601 time string with optional timezone.
356
- * Returns a Date object (with 1970-01-01 as date) if valid, otherwise undefined.
357
- *
358
- * @param {string} str - The time string to parse
359
- * @param {any} [def=undefined] - Default value to return if invalid
360
- * @returns {Date|undefined} - The parsed Date or default value
361
- */
362
- export function getDateTypeOfISOTime(str, def = undefined) {
363
- if (!isValidISOTime(str))
364
- return def;
365
-
366
- // Prepend a dummy date for parsing
367
- const dateTimeStr = CONST_TIME_INSERTDATE + str;
368
- const date = new Date(Date.parse(dateTimeStr));
369
- return isNaN(date.getTime()) ? def : date;
370
- }
371
- //#endregion