@pistonite/pure 0.26.8 → 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.
@@ -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
 
@@ -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) {
@@ -6,6 +6,8 @@ import Deque from "denque";
6
6
  * Only guaranteed if no one else has reference to the inner object
7
7
  *
8
8
  * It can take a second type parameter to specify interface with write methods
9
+ *
10
+ * @deprecated unstable API
9
11
  */
10
12
  export class RwLock<TRead, TWrite extends TRead = TRead> {
11
13
  /**
@@ -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
- (inputs: [number][], output: number): number[] => {
86
- // not actual meaningful unbatching
87
- return [output / inputs.length, output / inputs.length];
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,
@@ -0,0 +1,33 @@
1
+ const captured = new Set<unknown>();
2
+
3
+ /**
4
+ * Execute an async closure `fn`, and guarantee that `obj` will not be
5
+ * garbage-collected, until the promise is resolved.
6
+ */
7
+ export const scopedCapture = async <T>(fn: () => Promise<T>, obj: unknown): Promise<T> => {
8
+ // captures the object
9
+ // technically, this is not needed, as the delete() call above
10
+ // should make sure the captured object is not GC'ed.
11
+ // However, making it reachable from a global object will definitely
12
+ // prevent GC even with crazy optimization from any runtime
13
+ captured.add(obj);
14
+ try {
15
+ return await fn();
16
+ } finally {
17
+ captured.delete(obj);
18
+ }
19
+ };
20
+
21
+ /**
22
+ * Execute a closure `fn`, and guarantee that `obj` will not be
23
+ * garbage-collected during the execution
24
+ */
25
+ export const scopedCaptureSync = <T>(fn: () => T, obj: unknown): T => {
26
+ // captures the object
27
+ captured.add(obj);
28
+ try {
29
+ return fn();
30
+ } finally {
31
+ captured.delete(obj);
32
+ }
33
+ };
@@ -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 is guaranteed to:
package/src/sync/index.ts CHANGED
@@ -15,6 +15,7 @@ export { debounce, type DebounceConstructor } from "./debounce.ts";
15
15
  export { batch, type BatchConstructor } from "./batch.ts";
16
16
  export { once, type OnceConstructor } from "./once.ts";
17
17
  export { makePromise, type PromiseHandle } from "./util.ts";
18
+ export { scopedCapture, scopedCaptureSync } from "./capture.ts";
18
19
 
19
20
  // helper types
20
21
  export type { AnyFn, AwaitRet } from "./util.ts";
@@ -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;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Non-reentrant mutex
3
+ *
4
+ * This allows only one context to enter a block at a time in a FIFO manner.
5
+ *
6
+ * This mutex is non-reentrant. Trying to lock it again while the same
7
+ * context already owns the lock will cause a dead lock.
8
+ *
9
+ * While a context id can be used to implement reentrant locks,
10
+ * it is very cumbersome to use. https://github.com/tc39/proposal-async-context
11
+ * will allow for a cleaner implementation.
12
+ */
13
+ // TODO: implement it if needed
14
+ // export class Mutex {
15
+ // private waiters: Deque;
16
+ //
17
+ // constructor() {
18
+ // this.waiters = new
19
+ // }
20
+ //
21
+ // }
@@ -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 () => {
@@ -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