@fable-org/fable-library-js 2.2.0 → 2.3.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/Async.js +18 -0
- package/CHANGELOG.md +17 -1
- package/Reflection.js +13 -3
- package/String.js +24 -0
- package/Task.js +27 -0
- package/TaskBuilder.js +72 -0
- package/package.json +1 -1
package/Async.js
CHANGED
|
@@ -66,6 +66,24 @@ export function awaitPromise(p) {
|
|
|
66
66
|
return fromContinuations((conts) => p.then(conts[0]).catch((err) => (err instanceof OperationCanceledException
|
|
67
67
|
? conts[2] : conts[1])(err)));
|
|
68
68
|
}
|
|
69
|
+
export function awaitEvent(event, cancelAction) {
|
|
70
|
+
return protectedCont((ctx) => {
|
|
71
|
+
let tokenId;
|
|
72
|
+
const handler = ((_sender, arg) => {
|
|
73
|
+
ctx.cancelToken.removeListener(tokenId);
|
|
74
|
+
event.RemoveHandler(handler);
|
|
75
|
+
ctx.onSuccess(arg);
|
|
76
|
+
});
|
|
77
|
+
tokenId = ctx.cancelToken.addListener(() => {
|
|
78
|
+
event.RemoveHandler(handler);
|
|
79
|
+
if (cancelAction != null) {
|
|
80
|
+
cancelAction();
|
|
81
|
+
}
|
|
82
|
+
ctx.onCancel(new OperationCanceledException());
|
|
83
|
+
});
|
|
84
|
+
event.AddHandler(handler);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
69
87
|
export function cancellationToken() {
|
|
70
88
|
return protectedCont((ctx) => ctx.onSuccess(ctx.cancelToken));
|
|
71
89
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
last_commit_released:
|
|
2
|
+
last_commit_released: c977d78b39225a51c7bd051a1fe363ed0ccbe201
|
|
3
3
|
updaters:
|
|
4
4
|
- package.json:
|
|
5
5
|
file: package.json
|
|
@@ -15,6 +15,22 @@ All notable changes to this project will be documented in this file.
|
|
|
15
15
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
16
16
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
17
17
|
|
|
18
|
+
## 2.3.0 - 2026-06-30
|
|
19
|
+
|
|
20
|
+
### 🚀 Features
|
|
21
|
+
|
|
22
|
+
* *(js/ts)* Map task { } to Promise<T> ([97f54d36](https://github.com/fable-compiler/Fable/commit/97f54d3692e5ba881c249b289c20b4fad5ac27e2))
|
|
23
|
+
* *(js/ts/python/beam)* Add support for `Async.AwaitEvent` (#4693) ([71c98179](https://github.com/fable-compiler/Fable/commit/71c981792b6ff99cd417bb2e70a13a9d55ed72cf))
|
|
24
|
+
|
|
25
|
+
### 🐞 Bug Fixes
|
|
26
|
+
|
|
27
|
+
* *(js)* Respect StringComparison in String.IndexOf/LastIndexOf (#4681) ([7cb92d52](https://github.com/fable-compiler/Fable/commit/7cb92d522e5dc8dfe51351565504c59a94c2f408))
|
|
28
|
+
* *(js/ts)* Int list is not a union type (#4698) ([ecbecbe7](https://github.com/fable-compiler/Fable/commit/ecbecbe756610fd83a621c005e97eda92bbc332a))
|
|
29
|
+
* *(js/ts)* Pass TypeInfo to getRecordFields to handle None fields in anonymous records (#4704) ([c977d78b](https://github.com/fable-compiler/Fable/commit/c977d78b39225a51c7bd051a1fe363ed0ccbe201))
|
|
30
|
+
* *(ts)* Widen isEnumDefined value parameter to accept any value (#4690) ([0d6498dd](https://github.com/fable-compiler/Fable/commit/0d6498ddf65e0e18918690633da23b04b78479f6))
|
|
31
|
+
|
|
32
|
+
<strong><small>[View changes on Github](https://github.com/fable-compiler/Fable/compare/c9b3ee2429a4688946c1936e27df730837428070..c977d78b39225a51c7bd051a1fe363ed0ccbe201)</small></strong>
|
|
33
|
+
|
|
18
34
|
## 2.2.0 - 2026-06-24
|
|
19
35
|
|
|
20
36
|
### 🚀 Features
|
package/Reflection.js
CHANGED
|
@@ -93,7 +93,11 @@ export function option_type(generic) {
|
|
|
93
93
|
return t;
|
|
94
94
|
}
|
|
95
95
|
export function list_type(generic) {
|
|
96
|
-
|
|
96
|
+
const t = new TypeInfo("Microsoft.FSharp.Collections.FSharpList`1", [generic], undefined, undefined, undefined, () => [
|
|
97
|
+
new CaseInfo(t, 0, "Empty"),
|
|
98
|
+
new CaseInfo(t, 1, "Cons", [["Head", generic], ["Tail", t]])
|
|
99
|
+
]);
|
|
100
|
+
return t;
|
|
97
101
|
}
|
|
98
102
|
export function array_type(generic) {
|
|
99
103
|
return new TypeInfo("[]", [generic]);
|
|
@@ -377,8 +381,14 @@ export function getUnionCaseFields(uci) {
|
|
|
377
381
|
}
|
|
378
382
|
// This is used as replacement of `FSharpValue.GetRecordFields`
|
|
379
383
|
// For `FSharpTypes.GetRecordFields` see `getRecordElements`
|
|
380
|
-
//
|
|
381
|
-
|
|
384
|
+
// TypeInfo is used when available to enumerate fields by name: anonymous record None fields
|
|
385
|
+
// are omitted from the JS object, so Object.keys alone would miss them.
|
|
386
|
+
// Boxed anonymous record would are still not covered but this is the best we can do for now
|
|
387
|
+
// without adding a __fields__ to every anonymous record being created
|
|
388
|
+
export function getRecordFields(v, t) {
|
|
389
|
+
if (t.fields != null) {
|
|
390
|
+
return t.fields().map(([key, _]) => v[key]);
|
|
391
|
+
}
|
|
382
392
|
return Object.keys(v).map((k) => v[k]);
|
|
383
393
|
}
|
|
384
394
|
export function getRecordField(v, field) {
|
package/String.js
CHANGED
|
@@ -96,6 +96,30 @@ export function contains(str, pattern, ic) {
|
|
|
96
96
|
}
|
|
97
97
|
return false;
|
|
98
98
|
}
|
|
99
|
+
export function indexOf(str, searchValue, comparison, startIndex = 0) {
|
|
100
|
+
if (comparison === StringComparison.Ordinal) { // fast path
|
|
101
|
+
return str.indexOf(searchValue, startIndex);
|
|
102
|
+
}
|
|
103
|
+
const len = searchValue.length;
|
|
104
|
+
for (let i = Math.max(startIndex, 0); i <= str.length - len; i++) {
|
|
105
|
+
if (cmp(str.slice(i, i + len), searchValue, comparison) === 0) {
|
|
106
|
+
return i;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return -1;
|
|
110
|
+
}
|
|
111
|
+
export function lastIndexOf(str, searchValue, comparison, startIndex = str.length - 1) {
|
|
112
|
+
if (comparison === StringComparison.Ordinal) { // fast path
|
|
113
|
+
return str.lastIndexOf(searchValue, startIndex);
|
|
114
|
+
}
|
|
115
|
+
const len = searchValue.length;
|
|
116
|
+
for (let i = Math.min(startIndex, str.length - len); i >= 0; i--) {
|
|
117
|
+
if (cmp(str.slice(i, i + len), searchValue, comparison) === 0) {
|
|
118
|
+
return i;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return -1;
|
|
122
|
+
}
|
|
99
123
|
export function indexOfAny(str, anyOf, ...args) {
|
|
100
124
|
if (str == null || str === "") {
|
|
101
125
|
return -1;
|
package/Task.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { OperationCanceledException } from "./AsyncBuilder.js";
|
|
2
|
+
export class TaskCompletionSource {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.task = new Promise((resolve, reject) => {
|
|
5
|
+
this._resolve = resolve;
|
|
6
|
+
this._reject = reject;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
SetResult(value) { this._resolve(value); }
|
|
10
|
+
SetException(error) { this._reject(error); }
|
|
11
|
+
SetCancelled() { this._reject(new OperationCanceledException()); }
|
|
12
|
+
get_Task() { return this.task; }
|
|
13
|
+
}
|
|
14
|
+
export function fromResult(value) {
|
|
15
|
+
return Promise.resolve(value);
|
|
16
|
+
}
|
|
17
|
+
export function zero() {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
// Task<T> = Promise<T> in JS/TS. GetAwaiter/GetResult return the Promise itself;
|
|
21
|
+
// callers should use Async.AwaitTask to extract the value in an async context.
|
|
22
|
+
export function getAwaiter(t) {
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
export function getResult(t) {
|
|
26
|
+
return t;
|
|
27
|
+
}
|
package/TaskBuilder.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { zero } from "./Task.js";
|
|
2
|
+
export class TaskBuilder {
|
|
3
|
+
Bind(computation, binder) {
|
|
4
|
+
return computation.then(binder);
|
|
5
|
+
}
|
|
6
|
+
Combine(computation1, computation2) {
|
|
7
|
+
return computation1.then(computation2);
|
|
8
|
+
}
|
|
9
|
+
Delay(generator) {
|
|
10
|
+
return generator;
|
|
11
|
+
}
|
|
12
|
+
For(sequence, body) {
|
|
13
|
+
const iter = sequence[Symbol.iterator]();
|
|
14
|
+
let cur = iter.next();
|
|
15
|
+
return this.While(() => !cur.done, this.Delay(() => {
|
|
16
|
+
const res = body(cur.value);
|
|
17
|
+
cur = iter.next();
|
|
18
|
+
return res;
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
Return(value) {
|
|
22
|
+
return Promise.resolve(value);
|
|
23
|
+
}
|
|
24
|
+
ReturnFrom(computation) {
|
|
25
|
+
return computation;
|
|
26
|
+
}
|
|
27
|
+
TryFinally(computation, compensation) {
|
|
28
|
+
try {
|
|
29
|
+
return computation().finally(compensation);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
compensation();
|
|
33
|
+
return Promise.reject(e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
TryWith(computation, catchHandler) {
|
|
37
|
+
try {
|
|
38
|
+
return computation().catch(catchHandler);
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
try {
|
|
42
|
+
return catchHandler(e);
|
|
43
|
+
}
|
|
44
|
+
catch (e2) {
|
|
45
|
+
return Promise.reject(e2);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
Using(resource, binder) {
|
|
50
|
+
return this.TryFinally(() => binder(resource), () => resource.Dispose());
|
|
51
|
+
}
|
|
52
|
+
While(guard, computation) {
|
|
53
|
+
return (async () => {
|
|
54
|
+
while (guard()) {
|
|
55
|
+
await computation();
|
|
56
|
+
}
|
|
57
|
+
})();
|
|
58
|
+
}
|
|
59
|
+
Zero() {
|
|
60
|
+
return zero();
|
|
61
|
+
}
|
|
62
|
+
Run(computation) {
|
|
63
|
+
try {
|
|
64
|
+
return computation();
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return Promise.reject(e);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export const singleton = new TaskBuilder();
|
|
72
|
+
export function task() { return singleton; }
|
package/package.json
CHANGED