@fable-org/fable-library-js 2.0.0 → 2.1.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ---
2
+ last_commit_released: 63bcd3d90f37cb3934edcc59b5f54f49ffab3896
3
+ updaters:
4
+ - package.json:
5
+ file: package.json
6
+ - regex:
7
+ file: ./../Fable.Transforms/Global/Compiler.fs
8
+ pattern: (?<=let JS_LIBRARY_VERSION = ").*(?=")
9
+ ---
10
+
1
11
  # Changelog
2
12
 
3
13
  All notable changes to this project will be documented in this file.
@@ -5,7 +15,24 @@ All notable changes to this project will be documented in this file.
5
15
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
16
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
17
 
8
- ## Unreleased
18
+ ## 2.1.0 - 2026-05-28
19
+
20
+ ### 🚀 Features
21
+
22
+ * *(js/ts/python)* Add missing standard DateTime format specifiers for JS/TS and Python (#4547) ([90f5eb7](https://github.com/fable-compiler/Fable/commit/90f5eb7f61710bc679f6fdec30ff5d864984ac08))
23
+ * *(js/ts/python)* Add missing StringBuilder.Append overloads for numeric types (#4568) ([b402c30](https://github.com/fable-compiler/Fable/commit/b402c30d04bfca8644314ba4b21894abfff6a713))
24
+
25
+ ### 🐞 Bug Fixes
26
+
27
+ * [JS/TS] Fix DatetimeOffset.ToString("s") (#4596) ([3ce6f3f](https://github.com/fable-compiler/Fable/commit/3ce6f3fd88cc7f67e34e2bbc311e722c55ffbf45))
28
+ * *(js/ts)* Fix datetime custom format off by one year (#4558) ([83bdbb5](https://github.com/fable-compiler/Fable/commit/83bdbb5b34e70eae203831a3a442d477d15911e1))
29
+ * *(js/ts)* Hex format specifier uses no padding unless precision is specified (#4603) ([ba9857a](https://github.com/fable-compiler/Fable/commit/ba9857a4dcbf7ab1f936241f6619fda43e039040))
30
+ * *(js/ts)* Fix Decimal.GetBits returning incorrect mantissa after round arithmetic result (#4561) ([345bcd4](https://github.com/fable-compiler/Fable/commit/345bcd4b99417b304c71059966126fad74cf3a97))
31
+ * *(js/ts)* Fix G/g format specifier corrupting exponential notation when trimming trailing zeros (#4587) ([773c098](https://github.com/fable-compiler/Fable/commit/773c098a8f7ac7e8db1d4e490dec1294a80321ee))
32
+ * *(js/ts/python)* Fix FSharpOption not recognized as union type in F# reflection (#4529) ([d78a37d](https://github.com/fable-compiler/Fable/commit/d78a37db9f4c25eb51fac8afcd320b4ea36c60a7))
33
+ * *(ts)* Enforce browser-only compatibility in fable-library-ts tsconfig (#4563) ([10c81c1](https://github.com/fable-compiler/Fable/commit/10c81c1361208eb61f5fa78c6704e6fccc068fb1))
34
+
35
+ <strong><small>[View changes on Github](https://github.com/fable-compiler/Fable/compare/b471dc16fc3b5132af77b5974d1669c9b8220cca..63bcd3d90f37cb3934edcc59b5f54f49ffab3896)</small></strong>
9
36
 
10
37
  ## 2.0.0 - 2026-04-21
11
38
 
package/Date.js CHANGED
@@ -254,13 +254,13 @@ function dateToStringWithCustomFormat(date, format, utc) {
254
254
  cursorPos += tokenLength;
255
255
  switch (tokenLength) {
256
256
  case 1:
257
- result += localizedDate.getFullYear() % 100;
257
+ result += year(localizedDate) % 100;
258
258
  break;
259
259
  case 2:
260
- result += padWithZeros(localizedDate.getFullYear() % 100, 2);
260
+ result += padWithZeros(year(localizedDate) % 100, 2);
261
261
  break;
262
262
  default:
263
- result += padWithZeros(localizedDate.getFullYear(), tokenLength);
263
+ result += padWithZeros(year(localizedDate), tokenLength);
264
264
  break;
265
265
  }
266
266
  break;
@@ -384,10 +384,32 @@ function dateToStringWithOffset(date, format) {
384
384
  switch (format) {
385
385
  case "D": return dateToString_D(d);
386
386
  case "d": return dateToString_d(d);
387
- case "T": return dateToString_T(toUniversalTime(d));
388
- case "t": return dateToString_t(toUniversalTime(d));
387
+ case "F": return dateToString_D(d) + " " + dateToString_T(d);
388
+ case "f": return dateToString_D(d) + " " + dateToString_t(d);
389
+ case "G": return dateToString_d(d) + " " + dateToString_T(d);
390
+ case "g": return dateToString_d(d) + " " + dateToString_t(d);
391
+ case "M":
392
+ case "m": return dateToString_M(d);
389
393
  case "O":
390
394
  case "o": return dateToISOStringWithOffset(d, (date.offset ?? 0));
395
+ case "R":
396
+ case "r": {
397
+ const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
398
+ return dateToString_R(utcDate);
399
+ }
400
+ case "s": return dateToString_s(toUniversalTime(d));
401
+ case "T": return dateToString_T(toUniversalTime(d));
402
+ case "t": return dateToString_t(toUniversalTime(d));
403
+ case "u": {
404
+ const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
405
+ return dateToString_u(utcDate);
406
+ }
407
+ case "U": {
408
+ const utcDate = DateTime(date.getTime(), DateTimeKind.Utc);
409
+ return dateToString_D(utcDate) + " " + dateToString_T(utcDate);
410
+ }
411
+ case "Y":
412
+ case "y": return dateToString_Y(d);
391
413
  default: throw new Exception("Unrecognized Date print format");
392
414
  }
393
415
  }
@@ -415,6 +437,44 @@ function dateToString_t(date) {
415
437
  return padWithZeros(hour(date), 2)
416
438
  + ":" + padWithZeros(minute(date), 2);
417
439
  }
440
+ // RFC 1123: "Thu, 01 Jan 2009 00:00:00 GMT" — always UTC
441
+ function dateToString_R(date) {
442
+ const utcDate = toUniversalTime(date);
443
+ return shortDays[dayOfWeek(utcDate)] + ", "
444
+ + padWithZeros(day(utcDate), 2) + " "
445
+ + shortMonths[month(utcDate) - 1] + " "
446
+ + year(utcDate) + " "
447
+ + padWithZeros(hour(utcDate), 2) + ":"
448
+ + padWithZeros(minute(utcDate), 2) + ":"
449
+ + padWithZeros(second(utcDate), 2) + " GMT";
450
+ }
451
+ // Sortable ISO 8601, no timezone: "2009-06-15T13:45:30"
452
+ function dateToString_s(date) {
453
+ return padWithZeros(year(date), 4) + "-"
454
+ + padWithZeros(month(date), 2) + "-"
455
+ + padWithZeros(day(date), 2) + "T"
456
+ + padWithZeros(hour(date), 2) + ":"
457
+ + padWithZeros(minute(date), 2) + ":"
458
+ + padWithZeros(second(date), 2);
459
+ }
460
+ // Universal sortable: "2009-06-15 13:45:30Z" — always UTC
461
+ function dateToString_u(date) {
462
+ const utcDate = toUniversalTime(date);
463
+ return padWithZeros(year(utcDate), 4) + "-"
464
+ + padWithZeros(month(utcDate), 2) + "-"
465
+ + padWithZeros(day(utcDate), 2) + " "
466
+ + padWithZeros(hour(utcDate), 2) + ":"
467
+ + padWithZeros(minute(utcDate), 2) + ":"
468
+ + padWithZeros(second(utcDate), 2) + "Z";
469
+ }
470
+ // Month/day (InvariantCulture "MMMM dd"): "June 15"
471
+ function dateToString_M(date) {
472
+ return longMonths[month(date) - 1] + " " + padWithZeros(day(date), 2);
473
+ }
474
+ // Year/month (InvariantCulture "yyyy MMMM"): "2009 June"
475
+ function dateToString_Y(date) {
476
+ return year(date) + " " + longMonths[month(date) - 1];
477
+ }
418
478
  function dateToStringWithKind(date, format) {
419
479
  const utc = date.kind === DateTimeKind.Utc;
420
480
  if (typeof format !== "string") {
@@ -424,11 +484,23 @@ function dateToStringWithKind(date, format) {
424
484
  switch (format) {
425
485
  case "D": return dateToString_D(date);
426
486
  case "d": return dateToString_d(date);
487
+ case "F": return dateToString_D(date) + " " + dateToString_T(date);
488
+ case "f": return dateToString_D(date) + " " + dateToString_t(date);
489
+ case "G": return dateToString_d(date) + " " + dateToString_T(date);
490
+ case "g": return dateToString_d(date) + " " + dateToString_t(date);
491
+ case "M":
492
+ case "m": return dateToString_M(date);
493
+ case "O":
494
+ case "o": return dateToISOString(date, utc);
495
+ case "R":
496
+ case "r": return dateToString_R(date);
497
+ case "s": return dateToString_s(date);
427
498
  case "T": return dateToString_T(date);
428
499
  case "t": return dateToString_t(date);
429
- case "O":
430
- case "o":
431
- return dateToISOString(date, utc);
500
+ case "u": return dateToString_u(date);
501
+ case "U": return dateToString_D(toUniversalTime(date)) + " " + dateToString_T(toUniversalTime(date));
502
+ case "Y":
503
+ case "y": return dateToString_Y(date);
432
504
  default:
433
505
  throw new Exception("Unrecognized Date print format");
434
506
  }
package/Decimal.js CHANGED
@@ -207,14 +207,16 @@ export function fromParts(low, mid, high, isNegative, scale) {
207
207
  }
208
208
  export function getBits(d) {
209
209
  const bitSize = 96;
210
- const decDigits = Uint8Array.from(d.c);
210
+ const decStr = d.toString();
211
+ const absStr = decStr[0] === "-" ? decStr.slice(1) : decStr;
212
+ const dotPos = absStr.indexOf(".");
213
+ const scale = dotPos < 0 ? 0 : absStr.length - dotPos - 1;
214
+ const mantissaStr = absStr.replace(".", "");
215
+ const decDigits = Uint8Array.from(mantissaStr, ch => ch.charCodeAt(0) - 48);
211
216
  const hexDigits = decimalToHex(decDigits, bitSize);
212
217
  const low = getInt32Bits(hexDigits, 0);
213
218
  const mid = getInt32Bits(hexDigits, 8);
214
219
  const high = getInt32Bits(hexDigits, 16);
215
- const decStr = d.toString();
216
- const dotPos = decStr.indexOf(".");
217
- const scale = dotPos < 0 ? 0 : decStr.length - dotPos - 1;
218
220
  const signExp = ((scale & 0x7F) << 16) | (d.s < 0 ? 0x80000000 : 0);
219
221
  return [low, mid, high, signExp];
220
222
  }
package/Reflection.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Record, Union } from "./Types.js";
2
2
  import { Exception, combineHashCodes, equalArraysWith, stringHash } from "./Util.js";
3
3
  import Decimal from "./Decimal.js";
4
+ import { Some, some } from "./Option.js";
4
5
  export class CaseInfo {
5
6
  constructor(declaringType, tag, name, fields) {
6
7
  this.declaringType = declaringType;
@@ -85,7 +86,11 @@ export function lambda_type(argType, returnType) {
85
86
  return new TypeInfo("Microsoft.FSharp.Core.FSharpFunc`2", [argType, returnType]);
86
87
  }
87
88
  export function option_type(generic) {
88
- return new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic]);
89
+ const t = new TypeInfo("Microsoft.FSharp.Core.FSharpOption`1", [generic], undefined, undefined, undefined, () => [
90
+ new CaseInfo(t, 0, "None"),
91
+ new CaseInfo(t, 1, "Some", [["value", generic]])
92
+ ]);
93
+ return t;
89
94
  }
90
95
  export function list_type(generic) {
91
96
  return new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic]);
@@ -351,6 +356,16 @@ export function isFunction(t) {
351
356
  // FSharpValue
352
357
  export function getUnionFields(v, t) {
353
358
  const cases = getUnionCases(t);
359
+ // Special handling for option types (None is undefined, Some is the value or a Some wrapper)
360
+ if (t.fullname === "Microsoft.FSharp.Core.FSharpOption`1") {
361
+ if (v == null) {
362
+ return [cases[0], []]; // None case
363
+ }
364
+ else {
365
+ const innerValue = v instanceof Some ? v.value : v;
366
+ return [cases[1], [innerValue]]; // Some case
367
+ }
368
+ }
354
369
  const case_ = cases[v.tag];
355
370
  if (case_ == null) {
356
371
  throw new Exception(`Cannot find case ${v.name} in union type`);
@@ -380,6 +395,10 @@ export function makeUnion(uci, values) {
380
395
  if (values.length !== expectedLength) {
381
396
  throw new Exception(`Expected an array of length ${expectedLength} but got ${values.length}`);
382
397
  }
398
+ // Special handling for option types
399
+ if (uci.declaringType.fullname === "Microsoft.FSharp.Core.FSharpOption`1") {
400
+ return uci.tag === 0 ? undefined : some(values[0]);
401
+ }
383
402
  const construct = uci.declaringType.construct;
384
403
  if (construct == null) {
385
404
  return {};
package/String.js CHANGED
@@ -382,11 +382,24 @@ export function format(str, ...args) {
382
382
  }
383
383
  break;
384
384
  case "g":
385
- case "G":
385
+ case "G": {
386
386
  rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
387
- // TODO: Check why some numbers are formatted with decimal part
388
- rep = trimEnd(trimEnd(rep, "0"), ".");
387
+ // Handle exponential notation: only trim trailing zeros from mantissa, not from exponent.
388
+ // .NET G format guarantees an exponent of at least 2 digits with an explicit sign (e.g. "E-07").
389
+ const eIdx = rep.indexOf("e");
390
+ if (eIdx >= 0) {
391
+ const mantissa = trimEnd(trimEnd(rep.slice(0, eIdx), "0"), ".");
392
+ const expSign = rep[eIdx + 1]; // toPrecision always emits "+" or "-"
393
+ const expDigits = rep.slice(eIdx + 2);
394
+ const paddedExpDigits = expDigits.length < 2 ? "0" + expDigits : expDigits;
395
+ const eChar = format === "G" ? "E" : "e";
396
+ rep = mantissa + eChar + expSign + paddedExpDigits;
397
+ }
398
+ else {
399
+ rep = trimEnd(trimEnd(rep, "0"), ".");
400
+ }
389
401
  break;
402
+ }
390
403
  case "n":
391
404
  case "N":
392
405
  precision = precision != null ? precision : 2;
@@ -419,7 +432,7 @@ export function format(str, ...args) {
419
432
  if (!isIntegral(rep)) {
420
433
  throw new Exception("Format specifier was invalid.");
421
434
  }
422
- precision = precision != null ? precision : 2;
435
+ precision = precision != null ? precision : 1;
423
436
  rep = padLeft(toHex(rep), precision, "0");
424
437
  if (format === "X") {
425
438
  rep = rep.toUpperCase();
package/System.Text.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { replace, format, replicate, substring, isNullOrEmpty, join } from "./String.js";
2
2
  import { class_type } from "./Reflection.js";
3
3
  import { toString } from "./Types.js";
4
- import { Exception, clear, int32ToString } from "./Util.js";
4
+ import { Exception, clear, int64ToString, int32ToString, int16ToString } from "./Util.js";
5
5
  import { setItem, item } from "./Array.js";
6
6
  export class StringBuilder {
7
7
  constructor(value, capacity) {
@@ -47,10 +47,42 @@ export function StringBuilder__Append_61B1CA(x, c, repeatCount) {
47
47
  void (x.buf.push(s));
48
48
  return x;
49
49
  }
50
+ export function StringBuilder__Append_Z510FF069(x, o) {
51
+ void (x.buf.push(o.toString()));
52
+ return x;
53
+ }
54
+ export function StringBuilder__Append_244D3E44(x, o) {
55
+ void (x.buf.push(o.toString()));
56
+ return x;
57
+ }
58
+ export function StringBuilder__Append_Z524259E6(x, o) {
59
+ void (x.buf.push(int16ToString(o)));
60
+ return x;
61
+ }
62
+ export function StringBuilder__Append_Z6EF82811(x, o) {
63
+ void (x.buf.push(o.toString()));
64
+ return x;
65
+ }
50
66
  export function StringBuilder__Append_Z524259A4(x, o) {
51
67
  void (x.buf.push(int32ToString(o)));
52
68
  return x;
53
69
  }
70
+ export function StringBuilder__Append_Z6EF827D7(x, o) {
71
+ void (x.buf.push(o.toString()));
72
+ return x;
73
+ }
74
+ export function StringBuilder__Append_Z524259C1(x, o) {
75
+ void (x.buf.push(int64ToString(o)));
76
+ return x;
77
+ }
78
+ export function StringBuilder__Append_Z6EF827B6(x, o) {
79
+ void (x.buf.push(o.toString()));
80
+ return x;
81
+ }
82
+ export function StringBuilder__Append_Z7138B98C(x, o) {
83
+ void (x.buf.push(o.toString()));
84
+ return x;
85
+ }
54
86
  export function StringBuilder__Append_5E38073B(x, o) {
55
87
  void (x.buf.push(o.toString()));
56
88
  return x;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "private": false,
4
4
  "type": "module",
5
5
  "name": "@fable-org/fable-library-js",
6
- "version": "2.0.0",
6
+ "version": "2.1.0",
7
7
  "description": "Core library used by F# projects compiled with fable.io",
8
8
  "author": "Fable Contributors",
9
9
  "license": "MIT",