@pistonite/pure 0.27.0 → 0.27.1
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/package.json +2 -2
- package/src/fs/FsFileImpl.ts +3 -9
- package/src/fs/FsFileStandaloneImplHandleAPI.ts +2 -8
- package/src/fs/FsImplEntryAPI.ts +12 -48
- package/src/fs/FsImplFileAPI.ts +3 -12
- package/src/fs/FsImplHandleAPI.ts +12 -47
- package/src/fs/FsOpen.ts +27 -71
- package/src/fs/FsOpenFile.ts +2 -7
- package/src/fs/FsSave.ts +3 -10
- package/src/fs/FsSupportStatus.ts +1 -5
- package/src/fs/index.ts +2 -11
- package/src/log/index.ts +1 -5
- package/src/log/logger.ts +2 -6
- package/src/memory/async_erc.ts +4 -9
- package/src/memory/cell.ts +2 -8
- package/src/memory/erc.test.ts +123 -126
- package/src/memory/erc.ts +3 -8
- package/src/memory/persist.ts +1 -4
- package/src/pref/device.ts +3 -12
- package/src/pref/locale.ts +6 -18
- package/src/result/index.ts +1 -3
- package/src/sync/batch.test.ts +4 -6
- package/src/sync/batch.ts +4 -21
- package/src/sync/capture.ts +1 -4
- package/src/sync/debounce.ts +1 -6
- package/src/sync/latest.ts +2 -12
- package/src/sync/serial.test.ts +1 -2
- package/src/sync/serial.ts +3 -12
- package/src/sync/util.ts +1 -3
package/src/memory/async_erc.ts
CHANGED
|
@@ -82,9 +82,7 @@ export type AsyncErcRef<TName, TRepr = number> = {
|
|
|
82
82
|
* @deprecated use Emp
|
|
83
83
|
*/
|
|
84
84
|
export type AsyncErcRefType<T> =
|
|
85
|
-
T extends AsyncErc<infer TName, infer TRepr>
|
|
86
|
-
? AsyncErcRef<TName, TRepr>
|
|
87
|
-
: never;
|
|
85
|
+
T extends AsyncErc<infer TName, infer TRepr> ? AsyncErcRef<TName, TRepr> : never;
|
|
88
86
|
|
|
89
87
|
/**
|
|
90
88
|
* @deprecated use Emp
|
|
@@ -121,12 +119,9 @@ export const makeAsyncErcType = <TName, TRepr>({
|
|
|
121
119
|
}: AsyncErcTypeConstructor<TName, TRepr>): ((
|
|
122
120
|
value: TRepr | undefined,
|
|
123
121
|
) => AsyncErc<TName, TRepr>) => {
|
|
124
|
-
const createStrongRef = (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
let weakRef:
|
|
128
|
-
| (AsyncErcRef<TName, TRepr> & { invalidate: () => void })
|
|
129
|
-
| undefined = undefined;
|
|
122
|
+
const createStrongRef = (value: TRepr | undefined): AsyncErc<TName, TRepr> => {
|
|
123
|
+
let weakRef: (AsyncErcRef<TName, TRepr> & { invalidate: () => void }) | undefined =
|
|
124
|
+
undefined;
|
|
130
125
|
const invalidateWeakRef = () => {
|
|
131
126
|
if (!weakRef) {
|
|
132
127
|
return;
|
package/src/memory/cell.ts
CHANGED
|
@@ -14,10 +14,7 @@ export type CellConstructor<T> = {
|
|
|
14
14
|
export type Cell<T> = {
|
|
15
15
|
get(): T;
|
|
16
16
|
set(value: T): void;
|
|
17
|
-
subscribe(
|
|
18
|
-
callback: (value: T) => void,
|
|
19
|
-
notifyImmediately?: boolean,
|
|
20
|
-
): () => void;
|
|
17
|
+
subscribe(callback: (value: T) => void, notifyImmediately?: boolean): () => void;
|
|
21
18
|
};
|
|
22
19
|
|
|
23
20
|
class CellImpl<T> implements Cell<T> {
|
|
@@ -41,10 +38,7 @@ class CellImpl<T> implements Cell<T> {
|
|
|
41
38
|
}
|
|
42
39
|
}
|
|
43
40
|
|
|
44
|
-
public subscribe(
|
|
45
|
-
callback: (value: T) => void,
|
|
46
|
-
notifyImmediately?: boolean,
|
|
47
|
-
): () => void {
|
|
41
|
+
public subscribe(callback: (value: T) => void, notifyImmediately?: boolean): () => void {
|
|
48
42
|
this.subscribers.push(callback);
|
|
49
43
|
const unsubscribe = () => {
|
|
50
44
|
const index = this.subscribers.indexOf(callback);
|
package/src/memory/erc.test.ts
CHANGED
|
@@ -119,143 +119,140 @@ describe.each`
|
|
|
119
119
|
indirection | allocator
|
|
120
120
|
${"single"} | ${new Allocator(false)}
|
|
121
121
|
${"double"} | ${new Allocator(true)}
|
|
122
|
-
`(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
allocator.cleanup();
|
|
127
|
-
});
|
|
122
|
+
`("Erc - $indirection indirection", ({ allocator }: { allocator: Allocator }) => {
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
allocator.cleanup();
|
|
125
|
+
});
|
|
128
126
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
it("allocate and deallocate correctly", () => {
|
|
128
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
129
|
+
expect(allocator.getValue(test.value)).toBe("Hello");
|
|
130
|
+
test.free();
|
|
131
|
+
allocator.expectNoLeak();
|
|
132
|
+
});
|
|
135
133
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
it("frees if assigned new value", () => {
|
|
135
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
136
|
+
test.assign(allocator.allocValue("World"));
|
|
137
|
+
expect(allocator.getValue(test.value)).toBe("World");
|
|
138
|
+
test.free();
|
|
139
|
+
allocator.expectNoLeak();
|
|
140
|
+
});
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
142
|
+
it("does not free when taking value", () => {
|
|
143
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
144
|
+
const raw = test.take();
|
|
145
|
+
expect(allocator.getValue(raw)).toBe("Hello");
|
|
146
|
+
expect(test.value).toBeUndefined();
|
|
147
|
+
if (raw === undefined) {
|
|
148
|
+
throw new Error("Raw value is undefined");
|
|
149
|
+
}
|
|
150
|
+
allocator.makeTestErc(raw).free();
|
|
151
|
+
allocator.expectNoLeak();
|
|
152
|
+
});
|
|
155
153
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
154
|
+
it("invalidates weak references on free", () => {
|
|
155
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
156
|
+
const testWeak = test.getWeak();
|
|
157
|
+
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
158
|
+
test.free();
|
|
159
|
+
expect(testWeak.value).toBeUndefined();
|
|
160
|
+
allocator.expectNoLeak();
|
|
161
|
+
});
|
|
164
162
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
it("invalidates weak references on assign", () => {
|
|
164
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
165
|
+
const testWeak = test.getWeak();
|
|
166
|
+
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
167
|
+
test.assign(allocator.allocValue("World"));
|
|
168
|
+
expect(testWeak.value).toBeUndefined();
|
|
169
|
+
test.free();
|
|
170
|
+
allocator.expectNoLeak();
|
|
171
|
+
});
|
|
174
172
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
173
|
+
it("handles assign and take of different references correctly", () => {
|
|
174
|
+
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
175
|
+
const test2 = allocator.makeTestErc(allocator.allocValue("World"));
|
|
176
|
+
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
177
|
+
expect(allocator.getValue(test2.value)).toBe("World");
|
|
178
|
+
test1.assign(test2.take());
|
|
179
|
+
expect(allocator.getValue(test1.value)).toBe("World");
|
|
180
|
+
test1.free();
|
|
181
|
+
allocator.expectNoLeak();
|
|
182
|
+
});
|
|
185
183
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
184
|
+
it("handles assign and take of same references correctly", () => {
|
|
185
|
+
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
186
|
+
const test2 = test1.getStrong();
|
|
187
|
+
test1.assign(test2.take());
|
|
188
|
+
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
189
|
+
test1.free();
|
|
190
|
+
test2.free(); // should be no-op
|
|
191
|
+
allocator.expectNoLeak();
|
|
192
|
+
});
|
|
195
193
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
194
|
+
it("assigning another Erc directly should cause double free", async () => {
|
|
195
|
+
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
196
|
+
const test2 = test1.getStrong();
|
|
197
|
+
test1.assign(test2.value);
|
|
198
|
+
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
199
|
+
test1.free();
|
|
202
200
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
const freeTest2 = async () => {
|
|
202
|
+
test2.free();
|
|
203
|
+
};
|
|
204
|
+
await expect(freeTest2).rejects.toThrow("Double free detected");
|
|
205
|
+
allocator.expectNoLeak();
|
|
206
|
+
});
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
208
|
+
it("handles assign and take of same references correctly (same Erc)", () => {
|
|
209
|
+
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
210
|
+
test1.assign(test1.take());
|
|
211
|
+
expect(allocator.getValue(test1.value)).toBe("Hello");
|
|
212
|
+
test1.free();
|
|
213
|
+
allocator.expectNoLeak();
|
|
214
|
+
});
|
|
217
215
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
216
|
+
it("assigning another Erc directly should cause double free (same Erc)", async () => {
|
|
217
|
+
const test1 = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
218
|
+
test1.assign(test1.value);
|
|
219
|
+
const getTest1Value = async () => {
|
|
220
|
+
allocator.getValue(test1.value);
|
|
221
|
+
};
|
|
222
|
+
await expect(getTest1Value).rejects.toThrow("Dangling pointer");
|
|
225
223
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
224
|
+
const freeTest1 = async () => {
|
|
225
|
+
test1.free();
|
|
226
|
+
};
|
|
227
|
+
await expect(freeTest1).rejects.toThrow("Double free detected");
|
|
228
|
+
allocator.expectNoLeak();
|
|
229
|
+
});
|
|
232
230
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
231
|
+
it("inc ref count with strong reference", () => {
|
|
232
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
233
|
+
const test2 = test.getStrong();
|
|
234
|
+
expect(allocator.getValue(test.value)).toBe("Hello");
|
|
235
|
+
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
236
|
+
test.free();
|
|
237
|
+
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
238
|
+
test2.free();
|
|
239
|
+
allocator.expectNoLeak();
|
|
240
|
+
});
|
|
243
241
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
);
|
|
242
|
+
it("inc ref count with strong reference from weak reference", () => {
|
|
243
|
+
const test = allocator.makeTestErc(allocator.allocValue("Hello"));
|
|
244
|
+
const testWeak = test.getWeak();
|
|
245
|
+
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
246
|
+
const test2 = testWeak.getStrong();
|
|
247
|
+
expect(allocator.getValue(testWeak.value)).toBe("Hello");
|
|
248
|
+
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
249
|
+
const test2Weak = test2.getWeak();
|
|
250
|
+
test.free();
|
|
251
|
+
expect(testWeak.value).toBeUndefined();
|
|
252
|
+
expect(allocator.getValue(test2.value)).toBe("Hello");
|
|
253
|
+
expect(allocator.getValue(test2Weak.value)).toBe("Hello");
|
|
254
|
+
test2.free();
|
|
255
|
+
expect(test2Weak.value).toBeUndefined();
|
|
256
|
+
allocator.expectNoLeak();
|
|
257
|
+
});
|
|
258
|
+
});
|
package/src/memory/erc.ts
CHANGED
|
@@ -78,8 +78,7 @@ export type ErcRef<TName, TRepr = number> = {
|
|
|
78
78
|
/**
|
|
79
79
|
* @deprecated use Emp
|
|
80
80
|
*/
|
|
81
|
-
export type ErcRefType<T> =
|
|
82
|
-
T extends Erc<infer TName, infer TRepr> ? ErcRef<TName, TRepr> : never;
|
|
81
|
+
export type ErcRefType<T> = T extends Erc<infer TName, infer TRepr> ? ErcRef<TName, TRepr> : never;
|
|
83
82
|
|
|
84
83
|
/**
|
|
85
84
|
* @deprecated use Emp
|
|
@@ -255,13 +254,9 @@ export const makeErcType = <TName, TRepr>({
|
|
|
255
254
|
marker,
|
|
256
255
|
free,
|
|
257
256
|
addRef,
|
|
258
|
-
}: ErcTypeConstructor<TName, TRepr>): ((
|
|
259
|
-
value: TRepr | undefined,
|
|
260
|
-
) => Erc<TName, TRepr>) => {
|
|
257
|
+
}: ErcTypeConstructor<TName, TRepr>): ((value: TRepr | undefined) => Erc<TName, TRepr>) => {
|
|
261
258
|
const createStrongRef = (value: TRepr | undefined): Erc<TName, TRepr> => {
|
|
262
|
-
let weakRef:
|
|
263
|
-
| (ErcRef<TName, TRepr> & { invalidate: () => void })
|
|
264
|
-
| undefined = undefined;
|
|
259
|
+
let weakRef: (ErcRef<TName, TRepr> & { invalidate: () => void }) | undefined = undefined;
|
|
265
260
|
const invalidateWeakRef = () => {
|
|
266
261
|
if (!weakRef) {
|
|
267
262
|
return;
|
package/src/memory/persist.ts
CHANGED
|
@@ -106,10 +106,7 @@ class PersistImpl<T> implements Persist<T> {
|
|
|
106
106
|
this.cell.set(value);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
public subscribe(
|
|
110
|
-
callback: (value: T) => void,
|
|
111
|
-
notifyImmediately?: boolean,
|
|
112
|
-
): () => void {
|
|
109
|
+
public subscribe(callback: (value: T) => void, notifyImmediately?: boolean): () => void {
|
|
113
110
|
return this.cell.subscribe(callback, notifyImmediately);
|
|
114
111
|
}
|
|
115
112
|
|
package/src/pref/device.ts
CHANGED
|
@@ -82,15 +82,9 @@ export type DisplayModeOptions<T extends string> = {
|
|
|
82
82
|
*
|
|
83
83
|
* Use this only if the display mode needs to be detected programmatically.
|
|
84
84
|
*/
|
|
85
|
-
export const initDisplayMode = <T extends string>(
|
|
86
|
-
options: DisplayModeOptions<T>,
|
|
87
|
-
) => {
|
|
85
|
+
export const initDisplayMode = <T extends string>(options: DisplayModeOptions<T>) => {
|
|
88
86
|
const detectCallback = () => {
|
|
89
|
-
const mode = options.detect(
|
|
90
|
-
window.innerWidth,
|
|
91
|
-
window.innerHeight,
|
|
92
|
-
isMobile(),
|
|
93
|
-
);
|
|
87
|
+
const mode = options.detect(window.innerWidth, window.innerHeight, isMobile());
|
|
94
88
|
displayMode.set(mode);
|
|
95
89
|
};
|
|
96
90
|
if (
|
|
@@ -115,10 +109,7 @@ export const addDisplayModeSubscriber = <T extends string>(
|
|
|
115
109
|
subscriber: (mode: T) => void,
|
|
116
110
|
notifyImmediately?: boolean,
|
|
117
111
|
) => {
|
|
118
|
-
return displayMode.subscribe(
|
|
119
|
-
subscriber as (x: string) => void,
|
|
120
|
-
notifyImmediately,
|
|
121
|
-
);
|
|
112
|
+
return displayMode.subscribe(subscriber as (x: string) => void, notifyImmediately);
|
|
122
113
|
};
|
|
123
114
|
|
|
124
115
|
/** Get the current display mode */
|
package/src/pref/locale.ts
CHANGED
|
@@ -5,9 +5,7 @@ import { serial } from "../sync/serial.ts";
|
|
|
5
5
|
let supportedLocales: readonly string[] = [];
|
|
6
6
|
let defaultLocale: string = "";
|
|
7
7
|
let settingLocale: string = ""; // if locale is being set (setLocale called)
|
|
8
|
-
let onBeforeChangeHook: (
|
|
9
|
-
newLocale: string,
|
|
10
|
-
) => Promise<Result<void, "cancel">> = () => {
|
|
8
|
+
let onBeforeChangeHook: (newLocale: string) => Promise<Result<void, "cancel">> = () => {
|
|
11
9
|
return Promise.resolve({} as Result<void, "cancel">);
|
|
12
10
|
};
|
|
13
11
|
const locale = persist<string>({
|
|
@@ -81,10 +79,7 @@ export type LocaleOptions<TLocale extends string> = {
|
|
|
81
79
|
*
|
|
82
80
|
* Note that this hook will not be called during initialization.
|
|
83
81
|
*/
|
|
84
|
-
onBeforeChange?: (
|
|
85
|
-
newLocale: string,
|
|
86
|
-
checkCancel: () => void,
|
|
87
|
-
) => void | Promise<void>;
|
|
82
|
+
onBeforeChange?: (newLocale: string, checkCancel: () => void) => void | Promise<void>;
|
|
88
83
|
};
|
|
89
84
|
|
|
90
85
|
/**
|
|
@@ -118,9 +113,7 @@ export type LocaleOptions<TLocale extends string> = {
|
|
|
118
113
|
* Changing the locale from React components is the same as from outside React,
|
|
119
114
|
* with `setLocale` or `i18next.changeLanguage`, depending on your setup.
|
|
120
115
|
*/
|
|
121
|
-
export const initLocale = <TLocale extends string>(
|
|
122
|
-
options: LocaleOptions<TLocale>,
|
|
123
|
-
): void => {
|
|
116
|
+
export const initLocale = <TLocale extends string>(options: LocaleOptions<TLocale>): void => {
|
|
124
117
|
if (options.onBeforeChange) {
|
|
125
118
|
const onBeforeChange = options.onBeforeChange;
|
|
126
119
|
onBeforeChangeHook = serial({
|
|
@@ -135,8 +128,7 @@ export const initLocale = <TLocale extends string>(
|
|
|
135
128
|
if (options.initial) {
|
|
136
129
|
_locale = options.initial;
|
|
137
130
|
} else {
|
|
138
|
-
_locale =
|
|
139
|
-
convertToSupportedLocale(getPreferredLocale()) || options.default;
|
|
131
|
+
_locale = convertToSupportedLocale(getPreferredLocale()) || options.default;
|
|
140
132
|
}
|
|
141
133
|
defaultLocale = options.default;
|
|
142
134
|
if (options.persist) {
|
|
@@ -221,9 +213,7 @@ export const setLocale = (newLocale: string): boolean => {
|
|
|
221
213
|
* console.log(convertToSupportedLocale("es")); // undefined
|
|
222
214
|
* ```
|
|
223
215
|
*/
|
|
224
|
-
export const convertToSupportedLocale = (
|
|
225
|
-
newLocale: string,
|
|
226
|
-
): string | undefined => {
|
|
216
|
+
export const convertToSupportedLocale = (newLocale: string): string | undefined => {
|
|
227
217
|
return convertToSupportedLocaleIn(newLocale, supportedLocales);
|
|
228
218
|
};
|
|
229
219
|
|
|
@@ -257,9 +247,7 @@ export const convertToSupportedLocaleIn = (
|
|
|
257
247
|
* This is a thin wrapper for `convertToSupportedLocale`.
|
|
258
248
|
* See that function for more details.
|
|
259
249
|
*/
|
|
260
|
-
export const convertToSupportedLocaleOrDefault = (
|
|
261
|
-
newLocale: string,
|
|
262
|
-
): string => {
|
|
250
|
+
export const convertToSupportedLocaleOrDefault = (newLocale: string): string => {
|
|
263
251
|
return convertToSupportedLocale(newLocale) || defaultLocale;
|
|
264
252
|
};
|
|
265
253
|
|
package/src/result/index.ts
CHANGED
|
@@ -186,9 +186,7 @@ export function tryCatch<T, E = unknown>(fn: () => T): Result<T, E> {
|
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
/** Wrap an async function with try-catch and return a Promise<Result>. */
|
|
189
|
-
export async function tryAsync<T, E = unknown>(
|
|
190
|
-
fn: () => Promise<T>,
|
|
191
|
-
): Promise<Result<T, E>> {
|
|
189
|
+
export async function tryAsync<T, E = unknown>(fn: () => Promise<T>): Promise<Result<T, E>> {
|
|
192
190
|
try {
|
|
193
191
|
return { val: await fn() };
|
|
194
192
|
} catch (e) {
|
package/src/sync/batch.test.ts
CHANGED
|
@@ -81,12 +81,10 @@ describe("batch", () => {
|
|
|
81
81
|
});
|
|
82
82
|
test("unbatch", async () => {
|
|
83
83
|
const fn = vi.fn();
|
|
84
|
-
const unbatch = vi.fn(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
);
|
|
84
|
+
const unbatch = vi.fn((inputs: [number][], output: number): number[] => {
|
|
85
|
+
// not actual meaningful unbatching
|
|
86
|
+
return [output / inputs.length, output / inputs.length];
|
|
87
|
+
});
|
|
90
88
|
const execute = batch({
|
|
91
89
|
fn: (x: number) => {
|
|
92
90
|
fn(x);
|
package/src/sync/batch.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type AnyFn,
|
|
3
|
-
makePromise,
|
|
4
|
-
type PromiseHandle,
|
|
5
|
-
type AwaitRet,
|
|
6
|
-
} from "./util.ts";
|
|
1
|
+
import { type AnyFn, makePromise, type PromiseHandle, type AwaitRet } from "./util.ts";
|
|
7
2
|
|
|
8
3
|
/**
|
|
9
4
|
* An async event wrapper that allows multiple calls in an interval
|
|
@@ -99,13 +94,7 @@ export function batch<TFn extends AnyFn>({
|
|
|
99
94
|
interval,
|
|
100
95
|
disregardExecutionTime,
|
|
101
96
|
}: BatchConstructor<TFn>) {
|
|
102
|
-
const impl = new BatchImpl(
|
|
103
|
-
fn,
|
|
104
|
-
batch,
|
|
105
|
-
unbatch,
|
|
106
|
-
interval,
|
|
107
|
-
!!disregardExecutionTime,
|
|
108
|
-
);
|
|
97
|
+
const impl = new BatchImpl(fn, batch, unbatch, interval, !!disregardExecutionTime);
|
|
109
98
|
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
110
99
|
}
|
|
111
100
|
|
|
@@ -124,10 +113,7 @@ export type BatchConstructor<TFn extends AnyFn> = {
|
|
|
124
113
|
*
|
|
125
114
|
* By default, each input will receive the same output from the batched call
|
|
126
115
|
*/
|
|
127
|
-
unbatch?: (
|
|
128
|
-
inputs: Parameters<TFn>[],
|
|
129
|
-
output: AwaitRet<TFn>,
|
|
130
|
-
) => AwaitRet<TFn>[];
|
|
116
|
+
unbatch?: (inputs: Parameters<TFn>[], output: AwaitRet<TFn>) => AwaitRet<TFn>[];
|
|
131
117
|
|
|
132
118
|
/**
|
|
133
119
|
* Interval between each batched call
|
|
@@ -148,10 +134,7 @@ class BatchImpl<TFn extends AnyFn> {
|
|
|
148
134
|
private fn: TFn,
|
|
149
135
|
private batch: (inputs: Parameters<TFn>[]) => Parameters<TFn>,
|
|
150
136
|
private unbatch:
|
|
151
|
-
| ((
|
|
152
|
-
input: Parameters<TFn>[],
|
|
153
|
-
output: AwaitRet<TFn>,
|
|
154
|
-
) => AwaitRet<TFn>[])
|
|
137
|
+
| ((input: Parameters<TFn>[], output: AwaitRet<TFn>) => AwaitRet<TFn>[])
|
|
155
138
|
| undefined,
|
|
156
139
|
private interval: number,
|
|
157
140
|
private disregardExecutionTime: boolean,
|
package/src/sync/capture.ts
CHANGED
|
@@ -4,10 +4,7 @@ const captured = new Set<unknown>();
|
|
|
4
4
|
* Execute an async closure `fn`, and guarantee that `obj` will not be
|
|
5
5
|
* garbage-collected, until the promise is resolved.
|
|
6
6
|
*/
|
|
7
|
-
export const scopedCapture = async <T>(
|
|
8
|
-
fn: () => Promise<T>,
|
|
9
|
-
obj: unknown,
|
|
10
|
-
): Promise<T> => {
|
|
7
|
+
export const scopedCapture = async <T>(fn: () => Promise<T>, obj: unknown): Promise<T> => {
|
|
11
8
|
// captures the object
|
|
12
9
|
// technically, this is not needed, as the delete() call above
|
|
13
10
|
// should make sure the captured object is not GC'ed.
|
package/src/sync/debounce.ts
CHANGED
package/src/sync/latest.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type AnyFn,
|
|
3
|
-
type AwaitRet,
|
|
4
|
-
makePromise,
|
|
5
|
-
type PromiseHandle,
|
|
6
|
-
} from "./util.ts";
|
|
1
|
+
import { type AnyFn, type AwaitRet, makePromise, type PromiseHandle } from "./util.ts";
|
|
7
2
|
|
|
8
3
|
/**
|
|
9
4
|
* An async event wrapper that always resolve to the result of the latest
|
|
@@ -112,12 +107,7 @@ export class LatestImpl<TFn extends AnyFn> {
|
|
|
112
107
|
if (this.pending) {
|
|
113
108
|
// pending means currentArgs is not undefined
|
|
114
109
|
const currentArgs = this.currentArgs as Parameters<TFn>;
|
|
115
|
-
const nextArgs = this.updateArgs(
|
|
116
|
-
currentArgs,
|
|
117
|
-
this.middleArgs,
|
|
118
|
-
args,
|
|
119
|
-
this.nextArgs,
|
|
120
|
-
);
|
|
110
|
+
const nextArgs = this.updateArgs(currentArgs, this.middleArgs, args, this.nextArgs);
|
|
121
111
|
if (this.areArgsEqual(nextArgs, currentArgs)) {
|
|
122
112
|
// do not schedule new call
|
|
123
113
|
this.nextArgs = undefined;
|
package/src/sync/serial.test.ts
CHANGED
|
@@ -5,8 +5,7 @@ import type { Result } from "../result/index.ts";
|
|
|
5
5
|
|
|
6
6
|
test("example", async () => {
|
|
7
7
|
// helper function to simulate async work
|
|
8
|
-
const wait = (ms: number) =>
|
|
9
|
-
new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
9
|
// Create the wrapped function
|
|
11
10
|
const doWork = serial({
|
|
12
11
|
fn: (checkCancel) => async () => {
|
package/src/sync/serial.ts
CHANGED
|
@@ -133,10 +133,7 @@ import type { AnyFn } from "./util.ts";
|
|
|
133
133
|
* If the underlying function throws, the exception will be re-thrown to the caller.
|
|
134
134
|
*/
|
|
135
135
|
|
|
136
|
-
export function serial<TFn extends AnyFn>({
|
|
137
|
-
fn,
|
|
138
|
-
onCancel,
|
|
139
|
-
}: SerialConstructor<TFn>) {
|
|
136
|
+
export function serial<TFn extends AnyFn>({ fn, onCancel }: SerialConstructor<TFn>) {
|
|
140
137
|
const impl = new SerialImpl(fn, onCancel);
|
|
141
138
|
return (...args: Parameters<TFn>) => impl.invoke(...args);
|
|
142
139
|
}
|
|
@@ -162,10 +159,7 @@ class SerialImpl<TFn extends AnyFn> {
|
|
|
162
159
|
private fn: SerialFnCreator<TFn>;
|
|
163
160
|
private onCancel: SerialEventCancelCallback;
|
|
164
161
|
|
|
165
|
-
constructor(
|
|
166
|
-
fn: SerialFnCreator<TFn>,
|
|
167
|
-
onCancel?: SerialEventCancelCallback,
|
|
168
|
-
) {
|
|
162
|
+
constructor(fn: SerialFnCreator<TFn>, onCancel?: SerialEventCancelCallback) {
|
|
169
163
|
this.fn = fn;
|
|
170
164
|
this.serial = 0n;
|
|
171
165
|
if (onCancel) {
|
|
@@ -212,10 +206,7 @@ type SerialFnCreator<T> = (checkCancel: CheckCancelFn, serial: SerialId) => T;
|
|
|
212
206
|
* The callback type passed to SerialEvent constructor to be called
|
|
213
207
|
* when the event is cancelled
|
|
214
208
|
*/
|
|
215
|
-
export type SerialEventCancelCallback = (
|
|
216
|
-
current: SerialId,
|
|
217
|
-
latest: SerialId,
|
|
218
|
-
) => void;
|
|
209
|
+
export type SerialEventCancelCallback = (current: SerialId, latest: SerialId) => void;
|
|
219
210
|
|
|
220
211
|
/** The error type received by caller when an event is cancelled */
|
|
221
212
|
export type SerialCancelToken = "cancel";
|
package/src/sync/util.ts
CHANGED
|
@@ -26,9 +26,7 @@ export type PromiseHandle<T> = {
|
|
|
26
26
|
|
|
27
27
|
/** Shorthand for Awaited<ReturnType<T>> */
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
-
export type AwaitRet<T> = T extends (...args: any[]) => infer R
|
|
30
|
-
? Awaited<R>
|
|
31
|
-
: never;
|
|
29
|
+
export type AwaitRet<T> = T extends (...args: any[]) => infer R ? Awaited<R> : never;
|
|
32
30
|
|
|
33
31
|
/** Type for any function */
|
|
34
32
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|